diff --git a/Makefile b/Makefile index 5cc1dcf7..5fd49793 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ VERSION ?= v0.8.1 IMG ?= docker.io/substratusai/controller-manager:${VERSION} IMG_GCPMANAGER ?= docker.io/substratusai/gcp-manager:${VERSION} +IMG_SCI_KIND ?= docker.io/substratusai/sci-kind:${VERSION} # Set to false if you don't want GPU nodepools created ATTACH_GPU_NODEPOOLS=true @@ -36,6 +37,7 @@ ifeq ($(UNAME_M),arm64) SKAFFOLD_ARCH := arm64 else ifeq ($(UNAME_M),x86_64) + PROTOC_ARCH := aarch_64 SKAFFOLD_ARCH := amd64 else PROTOC_ARCH := $(UNAME_M) @@ -289,7 +291,9 @@ installation-scripts: installation-manifests: manifests kustomize cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} cd config/gcpmanager && $(KUSTOMIZE) edit set image gcp-manager=${IMG_GCPMANAGER} - $(KUSTOMIZE) build config/install-gcp > install/kubernetes/system.yaml + cd config/sci-kind && $(KUSTOMIZE) edit set image sci=${IMG_SCI_KIND} + # TODO: Fix in another PR: + #$(KUSTOMIZE) build config/install-gcp > install/kubernetes/system.yaml $(KUSTOMIZE) build config/install-kind > install/kubernetes/kind/system.yaml .PHONY: prepare-release diff --git a/config/install-kind/config.yaml b/config/install-kind/config.yaml index 4d640739..5e5539e2 100644 --- a/config/install-kind/config.yaml +++ b/config/install-kind/config.yaml @@ -6,3 +6,4 @@ metadata: data: CLOUD: kind CLUSTER_NAME: substratus + PRINCIPAL: unused diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 3e333f4a..bfdbce3a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -6,6 +6,3 @@ images: - name: controller newName: docker.io/substratusai/controller-manager newTag: v0.8.1 -- name: gcp-manager - newName: docker.io/substratusai/gcp-manager - newTag: v0.6.5-alpha diff --git a/config/sci-kind/kustomization.yaml b/config/sci-kind/kustomization.yaml index eb5a6f79..54d636eb 100644 --- a/config/sci-kind/kustomization.yaml +++ b/config/sci-kind/kustomization.yaml @@ -1,11 +1,11 @@ resources: - - ../sci +- ../sci patches: - - path: deployment_patch.yaml - - path: service_patch.yaml +- path: deployment_patch.yaml +- path: service_patch.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - - name: sci - newName: docker.io/substratusai/sci-kind - newTag: v0.8.1 +- name: sci + newName: docker.io/substratusai/sci-kind + newTag: v0.8.1 diff --git a/examples/notebook/notebook.yaml b/examples/notebook/notebook.yaml index 27e9fd6e..4a2a80f1 100644 --- a/examples/notebook/notebook.yaml +++ b/examples/notebook/notebook.yaml @@ -4,6 +4,8 @@ metadata: name: example spec: image: substratusai/base:latest + model: + name: facebook-opt-125m params: abc: xyz x: 123 diff --git a/install/kubernetes/kind/system.yaml b/install/kubernetes/kind/system.yaml index 431f85f4..6c204ae0 100644 --- a/install/kubernetes/kind/system.yaml +++ b/install/kubernetes/kind/system.yaml @@ -1458,7 +1458,7 @@ subjects: --- apiVersion: v1 data: - allow-bundled-registry.sh: | + configure-cri.sh: | #!/usr/bin/env bash set -x @@ -1484,17 +1484,19 @@ data: EOF fi nsenter --target 1 --mount bash -c "systemctl is-active --quiet containerd && echo 'Restarting containerd' && systemctl restart containerd" + # Wait for containerd to be ready so that skaffold doesn't fail. + nsenter --target 1 --mount bash -c "while ! ctr -n k8s.io containers ls; do sleep 1; done" fi kind: ConfigMap metadata: - name: allow-bundled-registry-script + name: configure-cri namespace: substratus --- apiVersion: v1 data: - ARTIFACT_BUCKET_URL: tar:///bucket CLOUD: kind CLUSTER_NAME: substratus + PRINCIPAL: unused kind: ConfigMap metadata: name: system @@ -1676,7 +1678,7 @@ spec: app: registry spec: containers: - - image: registry:2 + - image: docker.io/library/registry:2 name: registry ports: - containerPort: 5000 @@ -1740,17 +1742,17 @@ apiVersion: apps/v1 kind: DaemonSet metadata: labels: - app: allow-bundled-registry - name: allow-bundled-registry + app: configure-cri + name: configure-cri namespace: substratus spec: selector: matchLabels: - app: allow-bundled-registry + app: configure-cri template: metadata: labels: - app: allow-bundled-registry + app: configure-cri spec: containers: - image: gcr.io/google_containers/pause @@ -1758,9 +1760,9 @@ spec: hostPID: true initContainers: - command: - - /scripts/allow-bundled-registry.sh + - /scripts/configure-cri.sh image: ubuntu:22.04 - name: allow-bundled-registry + name: configure-cri securityContext: privileged: true volumeMounts: @@ -1777,5 +1779,5 @@ spec: name: etc - configMap: defaultMode: 484 - name: allow-bundled-registry-script + name: configure-cri name: scripts diff --git a/install/kubernetes/system.yaml b/install/kubernetes/system.yaml index 9306bac9..e69de29b 100644 --- a/install/kubernetes/system.yaml +++ b/install/kubernetes/system.yaml @@ -1,1686 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null - name: datasets.substratus.ai -spec: - group: substratus.ai - names: - categories: - - ai - kind: Dataset - listKind: DatasetList - plural: datasets - shortNames: - - data - singular: dataset - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ready - name: Ready - type: boolean - name: v1 - schema: - openAPIV3Schema: - description: "The Dataset API is used to describe data that can be referenced - for training Models. \n - Datasets pull in remote data sources using containerized - data loaders. \n - Users can specify their own ETL logic by referencing - a repository from a Dataset. \n - Users can leverage pre-built data loader - integrations with various sources. \n - Training typically requires a large - dataset. The Dataset API pulls a dataset once and stores it in a bucket, - which is mounted directly into training Jobs. \n - The Dataset API allows - users to query ready-to-use datasets (`kubectl get datasets`). \n - The - Dataset API allows Kubernetes RBAC to be applied as a mechanism for controlling - access to data." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec is the desired state of the Dataset. - properties: - build: - description: Build specifies how to build an image. - properties: - git: - description: Git is a reference to a git repository that will - be built within the cluster. Built image will be set in the - .spec.image field. - properties: - branch: - description: Branch is the git branch to use. Choose either - branch or tag. This branch will be pulled only at build - time and not monitored for changes. - type: string - path: - description: Path within the git repository referenced by - url. - type: string - tag: - description: Tag is the git tag to use. Choose either tag - or branch. This tag will be pulled only at build time and - not monitored for changes. - type: string - url: - description: 'URL to the git repository to build. Example: - https://github.com/my-username/my-repo' - type: string - required: - - url - type: object - x-kubernetes-map-type: atomic - upload: - description: Upload can be set to request to start an upload flow - where the client is responsible for uploading a local directory - that is to be built in the cluster. - properties: - md5Checksum: - description: MD5Checksum is the md5 checksum of the tar'd - repo root requested to be uploaded and built. - maxLength: 32 - minLength: 32 - pattern: ^[a-fA-F0-9]{32}$ - type: string - requestID: - description: RequestID is the ID of the request to build the - image. Changing this ID to a new value can be used to get - a new signed URL (useful when a URL has expired). - type: string - required: - - md5Checksum - - requestID - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-map-type: atomic - command: - description: Command to run in the container. - items: - type: string - type: array - image: - description: Image that contains dataset loading code and dependencies. - type: string - params: - additionalProperties: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - description: Params will be passed into the loading process as environment - variables. - type: object - resources: - description: Resources are the compute resources required by the container. - properties: - cpu: - default: 2 - description: CPU resources. - format: int64 - type: integer - disk: - default: 10 - description: Disk size in Gigabytes. - format: int64 - type: integer - gpu: - description: GPU resources. - properties: - count: - description: Count is the number of GPUs. - format: int64 - type: integer - type: - description: Type of GPU. - type: string - type: object - memory: - default: 10 - description: Memory is the amount of RAM in Gigabytes. - format: int64 - type: integer - type: object - type: object - status: - description: Status is the observed state of the Dataset. - properties: - artifacts: - description: Artifacts status. - properties: - url: - type: string - type: object - buildUpload: - description: BuildUpload contains the status of the build context - upload. - properties: - expiration: - description: Expiration is the time at which the signed URL expires. - format: date-time - type: string - requestID: - description: RequestID is the request id that corresponds to this - status. Clients should check that this matches the request id - that they set in the upload spec before uploading. - type: string - signedURL: - description: SignedURL is a short lived HTTPS URL. The client - is expected to send a PUT request to this URL containing a tar'd - docker build context. Content-Type of "application/octet-stream" - should be used. - type: string - storedMD5Checksum: - description: StoredMD5Checksum is the md5 checksum of the file - that the controller observed in storage. - type: string - type: object - conditions: - description: Conditions is the list of conditions that describe the - current state of the Dataset. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - ready: - default: false - description: Ready indicates that the Dataset is ready to use. See - Conditions for more details. - type: boolean - required: - - ready - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null - name: models.substratus.ai -spec: - group: substratus.ai - names: - categories: - - ai - kind: Model - listKind: ModelList - plural: models - singular: model - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ready - name: Ready - type: boolean - name: v1 - schema: - openAPIV3Schema: - description: "The Model API is used to build and train machine learning models. - \n - Base models can be built from a Git repository. \n - Models can be - trained by combining a base Model with a Dataset. \n - Model artifacts are - persisted in cloud buckets." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec is the desired state of the Model. - properties: - baseModel: - description: BaseModel should be set in order to mount another model - to be used for transfer learning. - properties: - name: - description: Name of Kubernetes object. - type: string - required: - - name - type: object - build: - description: Build specifies how to build an image. - properties: - git: - description: Git is a reference to a git repository that will - be built within the cluster. Built image will be set in the - .spec.image field. - properties: - branch: - description: Branch is the git branch to use. Choose either - branch or tag. This branch will be pulled only at build - time and not monitored for changes. - type: string - path: - description: Path within the git repository referenced by - url. - type: string - tag: - description: Tag is the git tag to use. Choose either tag - or branch. This tag will be pulled only at build time and - not monitored for changes. - type: string - url: - description: 'URL to the git repository to build. Example: - https://github.com/my-username/my-repo' - type: string - required: - - url - type: object - x-kubernetes-map-type: atomic - upload: - description: Upload can be set to request to start an upload flow - where the client is responsible for uploading a local directory - that is to be built in the cluster. - properties: - md5Checksum: - description: MD5Checksum is the md5 checksum of the tar'd - repo root requested to be uploaded and built. - maxLength: 32 - minLength: 32 - pattern: ^[a-fA-F0-9]{32}$ - type: string - requestID: - description: RequestID is the ID of the request to build the - image. Changing this ID to a new value can be used to get - a new signed URL (useful when a URL has expired). - type: string - required: - - md5Checksum - - requestID - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-map-type: atomic - command: - description: Command to run in the container. - items: - type: string - type: array - image: - description: Image that contains model code and dependencies. - type: string - params: - additionalProperties: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - description: Parameters are passing into the model training/loading - container as environment variables. Environment variable name will - be `"PARAM_" + uppercase(key)`. - type: object - resources: - description: Resources are the compute resources required by the container. - properties: - cpu: - default: 2 - description: CPU resources. - format: int64 - type: integer - disk: - default: 10 - description: Disk size in Gigabytes. - format: int64 - type: integer - gpu: - description: GPU resources. - properties: - count: - description: Count is the number of GPUs. - format: int64 - type: integer - type: - description: Type of GPU. - type: string - type: object - memory: - default: 10 - description: Memory is the amount of RAM in Gigabytes. - format: int64 - type: integer - type: object - trainingDataset: - description: Dataset to mount for training. - properties: - name: - description: Name of Kubernetes object. - type: string - required: - - name - type: object - type: object - status: - description: Status is the observed state of the Model. - properties: - artifacts: - description: Artifacts status. - properties: - url: - type: string - type: object - buildUpload: - description: BuildUpload contains the status of the build context - upload. - properties: - expiration: - description: Expiration is the time at which the signed URL expires. - format: date-time - type: string - requestID: - description: RequestID is the request id that corresponds to this - status. Clients should check that this matches the request id - that they set in the upload spec before uploading. - type: string - signedURL: - description: SignedURL is a short lived HTTPS URL. The client - is expected to send a PUT request to this URL containing a tar'd - docker build context. Content-Type of "application/octet-stream" - should be used. - type: string - storedMD5Checksum: - description: StoredMD5Checksum is the md5 checksum of the file - that the controller observed in storage. - type: string - type: object - conditions: - description: Conditions is the list of conditions that describe the - current state of the Model. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - ready: - default: false - description: Ready indicates that the Model is ready to use. See Conditions - for more details. - type: boolean - required: - - ready - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null - name: notebooks.substratus.ai -spec: - group: substratus.ai - names: - categories: - - ai - kind: Notebook - listKind: NotebookList - plural: notebooks - shortNames: - - nb - singular: notebook - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ready - name: Ready - type: boolean - name: v1 - schema: - openAPIV3Schema: - description: "The Notebook API can be used to quickly spin up a development - environment backed by high performance compute. \n - Notebooks integrate - with the Model and Dataset APIs allow for quick iteration. \n - Notebooks - can be synced to local directories to streamline developer experiences using - Substratus kubectl plugins." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec is the observed state of the Notebook. - properties: - build: - description: Build specifies how to build an image. - properties: - git: - description: Git is a reference to a git repository that will - be built within the cluster. Built image will be set in the - .spec.image field. - properties: - branch: - description: Branch is the git branch to use. Choose either - branch or tag. This branch will be pulled only at build - time and not monitored for changes. - type: string - path: - description: Path within the git repository referenced by - url. - type: string - tag: - description: Tag is the git tag to use. Choose either tag - or branch. This tag will be pulled only at build time and - not monitored for changes. - type: string - url: - description: 'URL to the git repository to build. Example: - https://github.com/my-username/my-repo' - type: string - required: - - url - type: object - x-kubernetes-map-type: atomic - upload: - description: Upload can be set to request to start an upload flow - where the client is responsible for uploading a local directory - that is to be built in the cluster. - properties: - md5Checksum: - description: MD5Checksum is the md5 checksum of the tar'd - repo root requested to be uploaded and built. - maxLength: 32 - minLength: 32 - pattern: ^[a-fA-F0-9]{32}$ - type: string - requestID: - description: RequestID is the ID of the request to build the - image. Changing this ID to a new value can be used to get - a new signed URL (useful when a URL has expired). - type: string - required: - - md5Checksum - - requestID - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-map-type: atomic - command: - description: Command to run in the container. - items: - type: string - type: array - dataset: - description: Dataset to load into the notebook container. - properties: - name: - description: Name of Kubernetes object. - type: string - required: - - name - type: object - image: - description: Image that contains notebook and dependencies. - type: string - model: - description: Model to load into the notebook container. - properties: - name: - description: Name of Kubernetes object. - type: string - required: - - name - type: object - params: - additionalProperties: - anyOf: - - type: integer - - type: string - x-kubernetes-int-or-string: true - description: Params will be passed into the notebook container as - environment variables. - type: object - resources: - description: Resources are the compute resources required by the container. - properties: - cpu: - default: 2 - description: CPU resources. - format: int64 - type: integer - disk: - default: 10 - description: Disk size in Gigabytes. - format: int64 - type: integer - gpu: - description: GPU resources. - properties: - count: - description: Count is the number of GPUs. - format: int64 - type: integer - type: - description: Type of GPU. - type: string - type: object - memory: - default: 10 - description: Memory is the amount of RAM in Gigabytes. - format: int64 - type: integer - type: object - suspend: - description: Suspend should be set to true to stop the notebook (Pod) - from running. This is a pointer to distinguish between explicit - false and not specified. - type: boolean - type: object - status: - description: Status is the observed state of the Notebook. - properties: - buildUpload: - description: BuildUpload contains the status of the build context - upload. - properties: - expiration: - description: Expiration is the time at which the signed URL expires. - format: date-time - type: string - requestID: - description: RequestID is the request id that corresponds to this - status. Clients should check that this matches the request id - that they set in the upload spec before uploading. - type: string - signedURL: - description: SignedURL is a short lived HTTPS URL. The client - is expected to send a PUT request to this URL containing a tar'd - docker build context. Content-Type of "application/octet-stream" - should be used. - type: string - storedMD5Checksum: - description: StoredMD5Checksum is the md5 checksum of the file - that the controller observed in storage. - type: string - type: object - conditions: - description: Conditions is the list of conditions that describe the - current state of the Notebook. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - ready: - default: false - description: Ready indicates that the Notebook is ready to serve. - See Conditions for more details. - type: boolean - required: - - ready - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null - name: servers.substratus.ai -spec: - group: substratus.ai - names: - categories: - - ai - kind: Server - listKind: ServerList - plural: servers - singular: server - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.ready - name: Ready - type: boolean - name: v1 - schema: - openAPIV3Schema: - description: The Server API is used to deploy a server that exposes the capabilities - of a Model via a HTTP interface. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec is the desired state of the Server. - properties: - build: - description: Build specifies how to build an image. - properties: - git: - description: Git is a reference to a git repository that will - be built within the cluster. Built image will be set in the - .spec.image field. - properties: - branch: - description: Branch is the git branch to use. Choose either - branch or tag. This branch will be pulled only at build - time and not monitored for changes. - type: string - path: - description: Path within the git repository referenced by - url. - type: string - tag: - description: Tag is the git tag to use. Choose either tag - or branch. This tag will be pulled only at build time and - not monitored for changes. - type: string - url: - description: 'URL to the git repository to build. Example: - https://github.com/my-username/my-repo' - type: string - required: - - url - type: object - x-kubernetes-map-type: atomic - upload: - description: Upload can be set to request to start an upload flow - where the client is responsible for uploading a local directory - that is to be built in the cluster. - properties: - md5Checksum: - description: MD5Checksum is the md5 checksum of the tar'd - repo root requested to be uploaded and built. - maxLength: 32 - minLength: 32 - pattern: ^[a-fA-F0-9]{32}$ - type: string - requestID: - description: RequestID is the ID of the request to build the - image. Changing this ID to a new value can be used to get - a new signed URL (useful when a URL has expired). - type: string - required: - - md5Checksum - - requestID - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-map-type: atomic - command: - description: Command to run in the container. - items: - type: string - type: array - image: - description: Image that contains model serving application and dependencies. - type: string - model: - description: Model references the Model object to be served. - properties: - name: - description: Name of Kubernetes object. - type: string - required: - - name - type: object - resources: - description: Resources are the compute resources required by the container. - properties: - cpu: - default: 2 - description: CPU resources. - format: int64 - type: integer - disk: - default: 10 - description: Disk size in Gigabytes. - format: int64 - type: integer - gpu: - description: GPU resources. - properties: - count: - description: Count is the number of GPUs. - format: int64 - type: integer - type: - description: Type of GPU. - type: string - type: object - memory: - default: 10 - description: Memory is the amount of RAM in Gigabytes. - format: int64 - type: integer - type: object - type: object - status: - description: Status is the observed state of the Server. - properties: - buildUpload: - description: Upload contains the status of the build context upload. - properties: - expiration: - description: Expiration is the time at which the signed URL expires. - format: date-time - type: string - requestID: - description: RequestID is the request id that corresponds to this - status. Clients should check that this matches the request id - that they set in the upload spec before uploading. - type: string - signedURL: - description: SignedURL is a short lived HTTPS URL. The client - is expected to send a PUT request to this URL containing a tar'd - docker build context. Content-Type of "application/octet-stream" - should be used. - type: string - storedMD5Checksum: - description: StoredMD5Checksum is the md5 checksum of the file - that the controller observed in storage. - type: string - type: object - conditions: - description: Conditions is the list of conditions that describe the - current state of the Server. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - ready: - default: false - description: Ready indicates whether the Server is ready to serve - traffic. See Conditions for more details. - type: boolean - required: - - ready - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: controller-manager-sa - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: serviceaccount - app.kubernetes.io/part-of: substratus - name: controller-manager - namespace: substratus ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: gcp-manager - namespace: substratus ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: gcp-manager-bootstrapper - namespace: substratus ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: leader-election-role - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: role - app.kubernetes.io/part-of: substratus - name: leader-election-role - namespace: substratus -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: service-account-annotator - namespace: substratus -rules: -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - patch - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - batch - resources: - - jobs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - substratus.ai - resources: - - datasets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - substratus.ai - resources: - - datasets/finalizers - verbs: - - update -- apiGroups: - - substratus.ai - resources: - - datasets/status - verbs: - - get - - patch - - update -- apiGroups: - - substratus.ai - resources: - - models - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - substratus.ai - resources: - - models/finalizers - verbs: - - update -- apiGroups: - - substratus.ai - resources: - - models/status - verbs: - - get - - patch - - update -- apiGroups: - - substratus.ai - resources: - - notebooks - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - substratus.ai - resources: - - notebooks/finalizers - verbs: - - update -- apiGroups: - - substratus.ai - resources: - - notebooks/status - verbs: - - get - - patch - - update -- apiGroups: - - substratus.ai - resources: - - servers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - substratus.ai - resources: - - servers/finalizers - verbs: - - update -- apiGroups: - - substratus.ai - resources: - - servers/status - verbs: - - get - - patch - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: clusterrole - app.kubernetes.io/part-of: substratus - name: metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: proxy-role - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: clusterrole - app.kubernetes.io/part-of: substratus - name: proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: leader-election-rolebinding - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: rolebinding - app.kubernetes.io/part-of: substratus - name: leader-election-rolebinding - namespace: substratus -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: leader-election-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: substratus ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: service-account-annotator-binding - namespace: substratus -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: service-account-annotator -subjects: -- kind: ServiceAccount - name: gcp-manager-bootstrapper - namespace: substratus ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: manager-rolebinding - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/part-of: substratus - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: substratus ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: proxy-rolebinding - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/part-of: substratus - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: substratus ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: service - app.kubernetes.io/part-of: substratus - control-plane: controller-manager - name: controller-manager-metrics-service - namespace: substratus -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - name: gcp-manager - namespace: substratus -spec: - ports: - - port: 10080 - protocol: TCP - targetPort: 10080 - selector: - app: gcp-manager ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: substratus - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: deployment - app.kubernetes.io/part-of: substratus - control-plane: controller-manager - name: controller-manager - namespace: substratus -spec: - replicas: 1 - selector: - matchLabels: - control-plane: controller-manager - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: manager - labels: - control-plane: controller-manager - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux - containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - envFrom: - - configMapRef: - name: system - image: docker.io/substratusai/controller-manager:v0.8.1 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - securityContext: - runAsNonRoot: true - serviceAccountName: controller-manager - terminationGracePeriodSeconds: 10 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gcp-manager - namespace: substratus -spec: - replicas: 1 - selector: - matchLabels: - app: gcp-manager - template: - metadata: - labels: - app: gcp-manager - spec: - containers: - - image: docker.io/substratusai/gcp-manager:v0.8.1 - imagePullPolicy: Always - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 15 - periodSeconds: 20 - successThreshold: 1 - tcpSocket: - port: 10080 - timeoutSeconds: 5 - name: gcp-manager - ports: - - containerPort: 10080 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - serviceAccountName: gcp-manager - terminationGracePeriodSeconds: 10 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: annotate-gcp-manager-sa - namespace: substratus -spec: - backoffLimit: 2 - template: - spec: - containers: - - command: - - /bin/bash - - -c - - | - # Get the project ID - PROJECT_ID=$(gcloud config get-value project) - # Get the current region - REGION=$(curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/zone | awk -F '/' '{print $4}' | sed 's/-[a-z]$//') - # Set the annotation value - ANNOTATION_VALUE="substratus-gcp-manager@${PROJECT_ID}.iam.gserviceaccount.com" - # Annotate the service account - kubectl annotate serviceaccount -n substratus gcp-manager iam.gke.io/gcp-service-account=${ANNOTATION_VALUE} - image: google/cloud-sdk:latest - name: gcloud - restartPolicy: OnFailure - serviceAccountName: gcp-manager-bootstrapper - ttlSecondsAfterFinished: 120 diff --git a/install/scripts/install-kubectl-plugins.sh b/install/scripts/install-kubectl-plugins.sh index 55fc7fd4..30b0d552 100755 --- a/install/scripts/install-kubectl-plugins.sh +++ b/install/scripts/install-kubectl-plugins.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -xe -version=v0.8.0 +version=v0.8.1 os=$(uname -s) arch=$(uname -m | sed 's/aarch64/arm64/g' | sed 's/x86_64/amd64/g') diff --git a/internal/cloud/common_test.go b/internal/cloud/common_test.go index 9391b5d7..2a908bbb 100644 --- a/internal/cloud/common_test.go +++ b/internal/cloud/common_test.go @@ -26,7 +26,7 @@ func TestCommon(t *testing.T) { require.EqualValues(t, cloud.Common{ ClusterName: "my-cluster", - ArtifactBucketURL: &cloud.BucketURL{Scheme: "gs", Bucket: "my-artifact-bucket"}, + ArtifactBucketURL: &cloud.BucketURL{Scheme: "gs", Bucket: "my-artifact-bucket", Path: ""}, RegistryURL: "gcr.io/my-project", Principal: "dummy-value", }, common) diff --git a/internal/cloud/gcp.go b/internal/cloud/gcp.go index 0686289e..88fdff48 100644 --- a/internal/cloud/gcp.go +++ b/internal/cloud/gcp.go @@ -111,7 +111,7 @@ func (gcp *GCP) MountBucket(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodS corev1.VolumeMount{ Name: req.Name, MountPath: "/content/" + mount.ContentSubdir, - SubPath: bktURL.Path + "/" + mount.BucketSubdir, + SubPath: strings.TrimPrefix(bktURL.Path+"/"+mount.BucketSubdir, "/"), ReadOnly: req.ReadOnly, }, ) diff --git a/internal/cloud/kind.go b/internal/cloud/kind.go index e9a4932c..d29342be 100644 --- a/internal/cloud/kind.go +++ b/internal/cloud/kind.go @@ -36,7 +36,7 @@ func (k *Kind) AutoConfigure(ctx context.Context) error { k.ArtifactBucketURL = &BucketURL{ Scheme: "tar", Bucket: "", - Path: "bucket", + Path: "/bucket", } } @@ -89,5 +89,6 @@ func (k *Kind) MountBucket(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSp return fmt.Errorf("container not found: %s", req.Container) } -func (k *Kind) AssociateServiceAccount(sa *corev1.ServiceAccount) { -} +func (k *Kind) AssociatePrincipal(*corev1.ServiceAccount) {} + +func (k Kind) GetPrincipal(*corev1.ServiceAccount) (string, bool) { return "", true } diff --git a/internal/cloud/utils.go b/internal/cloud/utils.go index d6de9b5c..41615f84 100644 --- a/internal/cloud/utils.go +++ b/internal/cloud/utils.go @@ -39,12 +39,10 @@ func ParseBucketURL(bktURL string) (*BucketURL, error) { } // NOTE: For local Kind clusters where URL is "tar:///bucket", u.Host will be empty. - bucket := u.Host - subpath := strings.TrimPrefix(u.Path, "/") return &BucketURL{ Scheme: u.Scheme, - Bucket: bucket, - Path: subpath, + Bucket: u.Host, + Path: strings.TrimPrefix(u.Path, "/"), }, nil } diff --git a/internal/controller/main_test.go b/internal/controller/main_test.go index 83829bdc..0809b727 100644 --- a/internal/controller/main_test.go +++ b/internal/controller/main_test.go @@ -81,7 +81,7 @@ func TestMain(m *testing.M) { testCloud.ProjectID = "test-project-id" testCloud.ClusterName = "test-cluster-name" testCloud.ClusterLocation = "us-central1" - testCloud.ArtifactBucketURL = &cloud.BucketURL{Scheme: "gs", Bucket: "test-artifact-bucket"} + testCloud.ArtifactBucketURL = &cloud.BucketURL{Scheme: "gs", Bucket: "test-artifact-bucket", Path: "/"} testCloud.RegistryURL = "registry.test" testCloud.Principal = "substratus@test-project-id.iam.gserviceaccount.com"