diff --git a/apis/resources/v1alpha1/bentorequest_types.go b/apis/resources/v1alpha1/bentorequest_types.go index eceeca2..bebe36f 100644 --- a/apis/resources/v1alpha1/bentorequest_types.go +++ b/apis/resources/v1alpha1/bentorequest_types.go @@ -59,7 +59,7 @@ type BentoRequestSpec struct { Models []BentoModel `json:"models,omitempty"` // +kubebuilder:validation:Optional - Image *string `json:"image,omitempty"` + Image string `json:"image,omitempty"` ImageBuildTimeout *time.Duration `json:"imageBuildTimeout,omitempty"` @@ -75,6 +75,9 @@ type BentoRequestSpec struct { // +kubebuilder:validation:Optional DockerConfigJSONSecretName string `json:"dockerConfigJsonSecretName,omitempty"` + // +kubebuilder:validation:Optional + OCIRegistryInsecure *bool `json:"ociRegistryInsecure,omitempty"` + // +kubebuilder:validation:Optional DownloaderContainerEnvFrom []corev1.EnvFromSource `json:"downloaderContainerEnvFrom,omitempty"` } @@ -92,6 +95,7 @@ type BentoRequestStatus struct { //+kubebuilder:subresource:status //+kubebuilder:printcolumn:name="Bento-Tag",type="string",JSONPath=".spec.bentoTag",description="Bento Tag" //+kubebuilder:printcolumn:name="Download-Url",type="string",JSONPath=".spec.downloadUrl",description="Download URL" +//+kubebuilder:printcolumn:name="Image",type="string",JSONPath=".spec.image",description="Image" //+kubebuilder:printcolumn:name="Image-Exists",type="string",JSONPath=".status.conditions[?(@.type=='ImageExists')].status",description="Image Exists" //+kubebuilder:printcolumn:name="Bento-Available",type="string",JSONPath=".status.conditions[?(@.type=='BentoAvailable')].status",description="Bento Available" //+kubebuilder:printcolumn:name="Image-Builder-Pod-Phase",type="string",JSONPath=".status.imageBuilderPodStatus.phase",description="Image Builder Pod Phase" diff --git a/apis/resources/v1alpha1/zz_generated.deepcopy.go b/apis/resources/v1alpha1/zz_generated.deepcopy.go index 9f51796..4bfa9d7 100644 --- a/apis/resources/v1alpha1/zz_generated.deepcopy.go +++ b/apis/resources/v1alpha1/zz_generated.deepcopy.go @@ -212,11 +212,6 @@ func (in *BentoRequestSpec) DeepCopyInto(out *BentoRequestSpec) { *out = make([]BentoModel, len(*in)) copy(*out, *in) } - if in.Image != nil { - in, out := &in.Image, &out.Image - *out = new(string) - **out = **in - } if in.ImageBuildTimeout != nil { in, out := &in.ImageBuildTimeout, &out.ImageBuildTimeout *out = new(timex.Duration) @@ -244,6 +239,11 @@ func (in *BentoRequestSpec) DeepCopyInto(out *BentoRequestSpec) { *out = new(v1.ResourceRequirements) (*in).DeepCopyInto(*out) } + if in.OCIRegistryInsecure != nil { + in, out := &in.OCIRegistryInsecure, &out.OCIRegistryInsecure + *out = new(bool) + **out = **in + } if in.DownloaderContainerEnvFrom != nil { in, out := &in.DownloaderContainerEnvFrom, &out.DownloaderContainerEnvFrom *out = make([]v1.EnvFromSource, len(*in)) diff --git a/config/crd/bases/resources.yatai.ai_bentorequests.yaml b/config/crd/bases/resources.yatai.ai_bentorequests.yaml index 1f940cf..176b30c 100644 --- a/config/crd/bases/resources.yatai.ai_bentorequests.yaml +++ b/config/crd/bases/resources.yatai.ai_bentorequests.yaml @@ -24,6 +24,10 @@ spec: jsonPath: .spec.downloadUrl name: Download-Url type: string + - description: Image + jsonPath: .spec.image + name: Image + type: string - description: Image Exists jsonPath: .status.conditions[?(@.type=='ImageExists')].status name: Image-Exists @@ -1360,6 +1364,8 @@ spec: - tag type: object type: array + ociRegistryInsecure: + type: boolean runners: items: properties: diff --git a/controllers/resources/bentorequest_controller.go b/controllers/resources/bentorequest_controller.go index 7661fc4..97dfae6 100644 --- a/controllers/resources/bentorequest_controller.go +++ b/controllers/resources/bentorequest_controller.go @@ -774,7 +774,75 @@ func getYataiClient(ctx context.Context) (yataiClient **yataiclient.YataiClient, return } -func getDockerRegistry(ctx context.Context, cliset *kubernetes.Clientset) (dockerRegistry modelschemas.DockerRegistrySchema, err error) { +func getDockerRegistry(ctx context.Context, bentoRequest *resourcesv1alpha1.BentoRequest, cliset *kubernetes.Clientset) (dockerRegistry modelschemas.DockerRegistrySchema, err error) { + if bentoRequest != nil && bentoRequest.Spec.DockerConfigJSONSecretName != "" { + var secret *corev1.Secret + secret, err = cliset.CoreV1().Secrets(bentoRequest.Namespace).Get(ctx, bentoRequest.Spec.DockerConfigJSONSecretName, metav1.GetOptions{}) + if err != nil { + err = errors.Wrapf(err, "get docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + configJSON, ok := secret.Data[".dockerconfigjson"] + if !ok { + err = errors.Errorf("docker config json secret %s does not have .dockerconfigjson key", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + var configObj struct { + Auths map[string]struct { + Auth string `json:"auth"` + } `json:"auths"` + } + err = json.Unmarshal(configJSON, &configObj) + if err != nil { + err = errors.Wrapf(err, "unmarshal docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + imageRegistryURI, _, _ := xstrings.Partition(bentoRequest.Spec.Image, "/") + var server string + var auth string + if imageRegistryURI != "" { + for k, v := range configObj.Auths { + if k == imageRegistryURI { + server = k + auth = v.Auth + break + } + } + if server == "" { + for k, v := range configObj.Auths { + if strings.Contains(k, imageRegistryURI) { + server = k + auth = v.Auth + break + } + } + } + } + if server == "" { + for k, v := range configObj.Auths { + server = k + auth = v.Auth + break + } + } + if server == "" { + err = errors.Errorf("no auth in docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + dockerRegistry.Server = server + var credentials []byte + credentials, err = base64.StdEncoding.DecodeString(auth) + if err != nil { + err = errors.Wrapf(err, "cannot base64 decode auth in docker config json secret %s", bentoRequest.Spec.DockerConfigJSONSecretName) + return + } + dockerRegistry.Username, _, dockerRegistry.Password = xstrings.Partition(string(credentials), ":") + if bentoRequest.Spec.OCIRegistryInsecure != nil { + dockerRegistry.Secure = !*bentoRequest.Spec.OCIRegistryInsecure + } + return + } + dockerRegistryConfig, err := commonconfig.GetDockerRegistryConfig(ctx, cliset) if err != nil { err = errors.Wrap(err, "get docker registry") @@ -820,8 +888,8 @@ func getDockerRegistry(ctx context.Context, cliset *kubernetes.Clientset) (docke } func getBentoImageName(bentoRequest *resourcesv1alpha1.BentoRequest, dockerRegistry modelschemas.DockerRegistrySchema, bentoRepositoryName, bentoVersion string, inCluster bool) string { - if bentoRequest != nil && bentoRequest.Spec.Image != nil && *bentoRequest.Spec.Image != "" { - return *bentoRequest.Spec.Image + if bentoRequest != nil && bentoRequest.Spec.Image != "" { + return bentoRequest.Spec.Image } var imageName string if inCluster { @@ -889,7 +957,7 @@ func (r *BentoRequestReconciler) getImageInfo(ctx context.Context, opt GetImageI err = errors.Wrap(err, "create kubernetes clientset") return } - dockerRegistry, err := getDockerRegistry(ctx, kubeCli) + dockerRegistry, err := getDockerRegistry(ctx, opt.BentoRequest, kubeCli) if err != nil { err = errors.Wrap(err, "get docker registry") return @@ -901,6 +969,9 @@ func (r *BentoRequestReconciler) getImageInfo(ctx context.Context, opt GetImageI imageInfo.DockerConfigJSONSecretName = opt.BentoRequest.Spec.DockerConfigJSONSecretName imageInfo.DockerRegistryInsecure = opt.BentoRequest.Annotations[commonconsts.KubeAnnotationDockerRegistryInsecure] == "true" + if opt.BentoRequest.Spec.OCIRegistryInsecure != nil { + imageInfo.DockerRegistryInsecure = *opt.BentoRequest.Spec.OCIRegistryInsecure + } if imageInfo.DockerConfigJSONSecretName == "" { var dockerRegistryConf *commonconfig.DockerRegistryConfig diff --git a/helm/yatai-image-builder-crds/templates/bentorequest.yaml b/helm/yatai-image-builder-crds/templates/bentorequest.yaml index b8d2c3b..1eb982b 100644 --- a/helm/yatai-image-builder-crds/templates/bentorequest.yaml +++ b/helm/yatai-image-builder-crds/templates/bentorequest.yaml @@ -120,6 +120,10 @@ spec: jsonPath: .spec.downloadUrl name: Download-Url type: string + - description: Image + jsonPath: .spec.image + name: Image + type: string - description: Image Exists jsonPath: .status.conditions[?(@.type=='ImageExists')].status name: Image-Exists @@ -898,6 +902,8 @@ spec: - tag type: object type: array + ociRegistryInsecure: + type: boolean runners: items: properties: diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 6dea091..b962b99 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -76,8 +76,8 @@ var _ = Describe("yatai-image-builder", Ordered, func() { if err != nil { return err } - if len(pods.Items) != 1 { - return fmt.Errorf("expected 1 pod, got %d", len(pods.Items)) + if len(pods.Items) != 2 { + return fmt.Errorf("expected 2 pod, got %d", len(pods.Items)) } pod := pods.Items[0] if pod.Status.Phase == corev1.PodFailed { @@ -98,7 +98,7 @@ var _ = Describe("yatai-image-builder", Ordered, func() { logrus.Infof("Getting BentoRequest CR %s", "test-bento") bentoRequest, err := bentorequestcli.BentoRequests("yatai").Get(ctx, "test-bento", metav1.GetOptions{}) - Expect(err).To(BeNil(), "failed to get BentoRequest CR") + Expect(err).To(BeNil(), "failed to get BentoRequest CR test-bento") logrus.Infof("Getting Bento CR %s", "test-bento") bento, err := bentorequestcli.Bentoes("yatai").Get(ctx, "test-bento", metav1.GetOptions{}) @@ -106,8 +106,23 @@ var _ = Describe("yatai-image-builder", Ordered, func() { return err } - Expect(bentoRequest.Spec.BentoTag).To(Equal(bento.Spec.Tag), "BentoRequest and Bento tag should match") - Expect(bento.Spec.Image).To(Not(BeEmpty()), "Bento CR image should not be empty") + Expect(bentoRequest.Spec.BentoTag).To(Equal(bento.Spec.Tag), "bentoRequest and bento tag should match") + Expect(bento.Spec.Image).To(Not(BeEmpty()), "bento CR image should not be empty") + + logrus.Infof("Getting BentoRequest CR %s", "test-bento1") + + bentoRequest1, err := bentorequestcli.BentoRequests("yatai").Get(ctx, "test-bento1", metav1.GetOptions{}) + Expect(err).To(BeNil(), "failed to get BentoRequest CR test-bento1") + + logrus.Infof("Getting Bento CR %s", "test-bento1") + bento1, err := bentorequestcli.Bentoes("yatai").Get(ctx, "test-bento1", metav1.GetOptions{}) + if err != nil { + return err + } + + Expect(bentoRequest1.Spec.BentoTag).To(Equal(bento1.Spec.Tag), "bentoRequest1 and bento1 tag should match") + Expect(bento1.Spec.Image).To(Not(BeEmpty()), "bento1 CR image should not be empty") + Expect(bentoRequest1.Spec.Image).To(Equal(bento1.Spec.Image), "bentoRequest1 and bento1 image should match") return nil }, time.Minute, time.Second).Should(Succeed()) }) diff --git a/tests/e2e/example.yaml b/tests/e2e/example.yaml index 7b0c4b6..37db94e 100644 --- a/tests/e2e/example.yaml +++ b/tests/e2e/example.yaml @@ -6,3 +6,15 @@ metadata: spec: bentoTag: iris_classifier:r4zint4b567i4usu234 downloadUrl: s3://yetone/bentos/test-bento.bento +--- +apiVersion: resources.yatai.ai/v1alpha1 +kind: BentoRequest +metadata: + name: test-bento1 + namespace: yatai +spec: + image: docker-registry-1.yatai-image-builder.svc.cluster.local:5000/test-custom-image:v1 + ociRegistryInsecure: true + dockerConfigJsonSecretName: regcred + bentoTag: iris_classifier:r4zint4b567i4usu234 + downloadUrl: s3://yetone/bentos/test-bento.bento diff --git a/tests/e2e/installation_test.sh b/tests/e2e/installation_test.sh index 5af1061..d18cd27 100755 --- a/tests/e2e/installation_test.sh +++ b/tests/e2e/installation_test.sh @@ -13,3 +13,21 @@ YATAI_ENDPOINT='empty' USE_LOCAL_HELM_CHART=true UPGRADE_CRDS=false AWS_SECRET_A echo "yatai-image-builder helm release values:" helm get values yatai-image-builder -n yatai-image-builder +helm upgrade --install docker-registry-1 twuni/docker-registry -n yatai-image-builder --set secrets.htpasswd='yetone:$2y$05$L7dlu/IGePjzo7C4YbmivOJxHs4jZ9k08D3CAAhxRQJoF62ey93yq' +cat <