diff --git a/api/v1alpha2/osartifact_types.go b/api/v1alpha2/osartifact_types.go index 507ddb2..5a273c1 100644 --- a/api/v1alpha2/osartifact_types.go +++ b/api/v1alpha2/osartifact_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha2 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -40,45 +41,101 @@ type OSArtifactSpec struct { // +kubebuilder:validation:Enum:=rpi3;rpi4 Model *Model `json:"model,omitempty"` - CloudConfigRef *SecretKeySelector `json:"cloudConfigRef,omitempty"` + // +optional + CloudConfigRef *corev1.SecretKeySelector `json:"cloudConfigRef,omitempty"` - Bundles []string `json:"bundles,omitempty"` + // +optional + Bundles []string `json:"bundles,omitempty"` + + // +optional FileBundles map[string]string `json:"fileBundles,omitempty"` - OutputImage *OutputImage `json:"outputImage,omitempty"` -} - -type SecretKeySelector struct { - Name string `json:"name"` + // Exporter when provided it will spawn an exporter job that + // pushes images built by the osbuilder to the provided registry. // +optional - Key string `json:"key,omitempty"` + Exporter *ExporterSpec `json:"exporter,omitempty"` } -type RegistryCloud string +type RegistryType string const ( - // RegistryCloudECR ensures that special env variables will be injected + // RegistryTypeECR ensures that special env variables will be injected // into the exporter job to allow kaniko to automatically auth with the // ecr registry to push the images. - RegistryCloudECR RegistryCloud = "ecr" - // RegistryCloudOther requires from user to provide username/password secret + RegistryTypeECR RegistryType = "ecr" + // RegistryTypeOther requires from user to provide username/password secret // in order for kaniko to be able to authenticate with the container registry. - RegistryCloudOther RegistryCloud = "other" + RegistryTypeOther RegistryType = "other" ) -type OutputImage struct { +type ExporterSpec struct { + // Registry is a registry spec used to push the final images built by the osbuilder. + // +required + Registry RegistrySpec `json:"registry"` + + // ServiceAccount allows overriding 'default' SA bound to the exporter pods. + // +optional + ServiceAccount *string `json:"serviceAccount,omitempty"` + + // ExtraEnvVars allows to append extra env vars to the exporter pods. + // +optional + ExtraEnvVars *[]corev1.EnvVar `json:"extraEnvVars,omitempty"` +} + +func (in *ExporterSpec) IsECRRegistry() bool { + return in.Registry.Type == RegistryTypeECR +} + +func (in *ExporterSpec) HasDockerConfigSecret() bool { + return in.Registry.DockerConfigSecretKeyRef != nil +} + +func (in *ExporterSpec) HasExtraEnvVars() bool { + return in.ExtraEnvVars != nil && len(*in.ExtraEnvVars) > 0 +} + +func (in *ExporterSpec) ServiceAccountName() string { + if in.ServiceAccount == nil || len(*in.ServiceAccount) == 0 { + // Default SA name. Always exists. + return "default" + } + + return *in.ServiceAccount +} + +type ImageSpec struct { + // Repository is the name of repository where image is being pushed. + // +required + Repository string `json:"repository"` + + // Tag is the tag name of the image being pushed. Defaults to 'latest' if not provided. + // +optional + Tag string `json:"tag,omitempty"` +} + +type RegistrySpec struct { + // Name is a DNS name of the registry. It has to be accessible by the pod. + // +required + Name string `json:"name"` + + // Type is a kind of registry being used. Currently supported values are: + // - ecr - Amazon Elastic Container Registry. Use only if a pod runs on + // an eks cluster and has permissions to push to the registry. + // - other - Any other type of the registry. It requires DockerConfigSecretKeyRef + // to be provided in order to auth to the registry. // +kubebuilder:validation:Enum=ecr;other // +kubebuilder:default=other // +required - Cloud RegistryCloud `json:"cloud"` + Type RegistryType `json:"type"` + + // Image defines the image details required to push image to the registry. + // +required + Image ImageSpec `json:"image"` + + // DockerConfigSecretKeyRef is a reference to the secret that holds the `config.json` auth file. + // It should be in a format that `docker login` can accept to auth to the registry. // +optional - Registry string `json:"registry,omitempty"` - // +optional - Repository string `json:"repository,omitempty"` - // +optional - Tag string `json:"tag,omitempty"` - // +optional - DockerConfigSecretKeyRef *SecretKeySelector `json:"dockerConfigSecretKeyRef,omitempty"` + DockerConfigSecretKeyRef *corev1.SecretKeySelector `json:"dockerConfigSecretKeyRef,omitempty"` } type ArtifactPhase string diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index a62ab7b..5585e09 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -21,10 +21,58 @@ limitations under the License. package v1alpha2 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterSpec) DeepCopyInto(out *ExporterSpec) { + *out = *in + in.Registry.DeepCopyInto(&out.Registry) + if in.ServiceAccount != nil { + in, out := &in.ServiceAccount, &out.ServiceAccount + *out = new(string) + **out = **in + } + if in.ExtraEnvVars != nil { + in, out := &in.ExtraEnvVars, &out.ExtraEnvVars + *out = new([]v1.EnvVar) + if **in != nil { + in, out := *in, *out + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterSpec. +func (in *ExporterSpec) DeepCopy() *ExporterSpec { + if in == nil { + return nil + } + out := new(ExporterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageSpec) DeepCopyInto(out *ImageSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec. +func (in *ImageSpec) DeepCopy() *ImageSpec { + if in == nil { + return nil + } + out := new(ImageSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OSArtifact) DeepCopyInto(out *OSArtifact) { *out = *in @@ -94,8 +142,8 @@ func (in *OSArtifactSpec) DeepCopyInto(out *OSArtifactSpec) { } if in.CloudConfigRef != nil { in, out := &in.CloudConfigRef, &out.CloudConfigRef - *out = new(SecretKeySelector) - **out = **in + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) } if in.Bundles != nil { in, out := &in.Bundles, &out.Bundles @@ -109,9 +157,9 @@ func (in *OSArtifactSpec) DeepCopyInto(out *OSArtifactSpec) { (*out)[key] = val } } - if in.OutputImage != nil { - in, out := &in.OutputImage, &out.OutputImage - *out = new(OutputImage) + if in.Exporter != nil { + in, out := &in.Exporter, &out.Exporter + *out = new(ExporterSpec) (*in).DeepCopyInto(*out) } } @@ -131,7 +179,7 @@ func (in *OSArtifactStatus) DeepCopyInto(out *OSArtifactStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) + *out = make([]metav1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -149,36 +197,22 @@ func (in *OSArtifactStatus) DeepCopy() *OSArtifactStatus { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *OutputImage) DeepCopyInto(out *OutputImage) { +func (in *RegistrySpec) DeepCopyInto(out *RegistrySpec) { *out = *in + out.Image = in.Image if in.DockerConfigSecretKeyRef != nil { in, out := &in.DockerConfigSecretKeyRef, &out.DockerConfigSecretKeyRef - *out = new(SecretKeySelector) - **out = **in + *out = new(v1.SecretKeySelector) + (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OutputImage. -func (in *OutputImage) DeepCopy() *OutputImage { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistrySpec. +func (in *RegistrySpec) DeepCopy() *RegistrySpec { if in == nil { return nil } - out := new(OutputImage) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector. -func (in *SecretKeySelector) DeepCopy() *SecretKeySelector { - if in == nil { - return nil - } - out := new(SecretKeySelector) + out := new(RegistrySpec) in.DeepCopyInto(out) return out } diff --git a/charts/osartifact/Chart.yaml b/charts/osartifact/Chart.yaml index 03ab5ac..22b2464 100644 --- a/charts/osartifact/Chart.yaml +++ b/charts/osartifact/Chart.yaml @@ -5,4 +5,4 @@ maintainers: - name: Plural email: support@plural.sh type: application -version: 0.5.1 \ No newline at end of file +version: 0.6.0 \ No newline at end of file diff --git a/charts/osartifact/templates/osartifact.yaml b/charts/osartifact/templates/osartifact.yaml index 0876493..92428c8 100644 --- a/charts/osartifact/templates/osartifact.yaml +++ b/charts/osartifact/templates/osartifact.yaml @@ -22,13 +22,19 @@ spec: cloudConfigRef: name: {{ include "osartifact.fullname" . }}-config key: cloud-config.yaml - outputImage: - cloud: {{ .Values.exporter.cloud }} - registry: {{ .Values.exporter.registry }} - repository: {{ .Values.exporter.repository }} - tag: {{ .Values.exporter.tag }} - {{- if (eq .Values.exporter.cloud "other") }} - passwordSecretKeyRef: - name: {{ .Values.exporter.configSecret.name | default (printf "%s-%s" (include "osartifact.fullname" .) "config") }} - key: {{ .Values.exporter.configSecret.key }} - {{- end }} \ No newline at end of file + exporter: + serviceAccount: {{ include "osartifact.fullname" . }} + {{- with .Values.exporter.extraEnvVars }} + extraEnvVars: {{ . | toYaml | nindent 4 }} + {{- end }} + registry: + name: {{ .Values.exporter.registry.name }} + type: {{ .Values.exporter.registry.type }} + image: + repository: {{ .Values.exporter.registry.image.repository }} + tag: {{ .Values.exporter.registry.image.tag }} + {{- if (eq .Values.exporter.registry.type "other") }} + passwordSecretKeyRef: + name: {{ .Values.exporter.registry.configSecret.name | default (printf "%s-%s" (include "osartifact.fullname" .) "config") }} + key: {{ .Values.exporter.registry.configSecret.key }} + {{- end }} \ No newline at end of file diff --git a/charts/osartifact/templates/secret.yaml b/charts/osartifact/templates/secret.yaml index 84c72c8..40cd574 100644 --- a/charts/osartifact/templates/secret.yaml +++ b/charts/osartifact/templates/secret.yaml @@ -95,7 +95,7 @@ stringData: { "auths": { "{{ .Values.exporter.registry }}": { - "auth": {{ printf "%s:%s" .Values.exporter.username $dockerUserPassword | b64enc | quote }} + "auth": {{ printf "%s:%s" .Values.exporter.registry.username $dockerUserPassword | b64enc | quote }} } } } diff --git a/charts/osartifact/templates/serviceaccount.yaml b/charts/osartifact/templates/serviceaccount.yaml new file mode 100644 index 0000000..4d19f4f --- /dev/null +++ b/charts/osartifact/templates/serviceaccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "osartifact.fullname" . }} + namespace: {{ .Release.Namespace }} + {{- with .Values.exporter.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} \ No newline at end of file diff --git a/charts/osartifact/values.yaml b/charts/osartifact/values.yaml index 68640e2..9bfc3d6 100644 --- a/charts/osartifact/values.yaml +++ b/charts/osartifact/values.yaml @@ -59,23 +59,34 @@ extraCloudConfig: ~ # Export configuration for the final ISO images exporter: - # Whether container registry is an ECR instance or other. One of: "ecr", "other" - cloud: other - # Docker registry DNS name where we should export packed ISO images - registry: ~ - # Name of the repository where images should be stored - repository: plural-edge - # Image tag that should be used when pushing to the registry - tag: latest - # Username used when generating docker config.json. It is only used when 'passwordSecret' is provided. - username: plural - # Secret that stores just the password for the docker registry user. - # One of 'passwordSecret' or 'configSecret' must be provided. - passwordSecret: + serviceAccount: + annotations: ~ + # Defines extra env vars that will be added to the exporter pob + # extraEnvVars: + # - name: MY_ENV_VAR + # value: myvalue + extraEnvVars: [] + registry: + # Container registry DNS name where we should export packed ISO images name: ~ - key: password - # Secret configuration that stores the docker config.json file with the auth information. - # It is in the default docker format. - configSecret: - name: ~ - key: config.json \ No newline at end of file + # Whether container registry is an ECR instance or other. One of: "ecr", "other" + type: other + + image: + # Name of the repository where images should be stored + repository: plural-edge + # Image tag that should be used when pushing to the registry + tag: latest + + # Username used when generating docker config.json. It is only used when 'passwordSecret' is provided. + username: plural + # Secret that stores just the password for the docker registry user. + # One of 'passwordSecret' or 'configSecret' must be provided. + passwordSecret: + name: ~ + key: password + # Secret configuration that stores the docker config.json file with the auth information. + # It is in the default docker format. + configSecret: + name: ~ + key: config.json \ No newline at end of file diff --git a/charts/osbuilder/Chart.yaml b/charts/osbuilder/Chart.yaml index 4b7a53e..8b9e471 100644 --- a/charts/osbuilder/Chart.yaml +++ b/charts/osbuilder/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 name: osbuilder description: A Helm chart for osbuilder -appVersion: 0.2.0 -version: 0.1.13 +appVersion: 0.3.0 +version: 0.1.14 dependencies: - name: cert-manager version: v1.16.3 diff --git a/charts/osbuilder/crds/build.kairos.io_osartifacts.yaml b/charts/osbuilder/crds/build.kairos.io_osartifacts.yaml index d5f1df6..367ea5e 100644 --- a/charts/osbuilder/crds/build.kairos.io_osartifacts.yaml +++ b/charts/osbuilder/crds/build.kairos.io_osartifacts.yaml @@ -48,13 +48,226 @@ spec: type: string type: array cloudConfigRef: + description: SecretKeySelector selects a key of a Secret. properties: key: + description: The key of the secret to select from. Must be a + valid secret key. type: string name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + exporter: + description: |- + Exporter when provided it will spawn an exporter job that + pushes images built by the osbuilder to the provided registry. + properties: + extraEnvVars: + description: ExtraEnvVars allows to append extra env vars to the + exporter pods. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + registry: + description: Registry is a registry spec used to push the final + images built by the osbuilder. + properties: + dockerConfigSecretKeyRef: + description: |- + DockerConfigSecretKeyRef is a reference to the secret that holds the `config.json` auth file. + It should be in a format that `docker login` can accept to auth to the registry. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + image: + description: Image defines the image details required to push + image to the registry. + properties: + repository: + description: Repository is the name of repository where + image is being pushed. + type: string + tag: + description: Tag is the tag name of the image being pushed. + Defaults to 'latest' if not provided. + type: string + required: + - repository + type: object + name: + description: Name is a DNS name of the registry. It has to + be accessible by the pod. + type: string + type: + default: other + description: "Type is a kind of registry being used. Currently + supported values are:\n\t- ecr \t- Amazon Elastic Container + Registry. Use only if a pod runs on\n\t\t\t an eks cluster + and has permissions to push to the registry.\n\t- other + - Any other type of the registry. It requires DockerConfigSecretKeyRef\n\t\t\t + \ to be provided in order to auth to the registry." + enum: + - ecr + - other + type: string + required: + - image + - name + - type + type: object + serviceAccount: + description: ServiceAccount allows overriding 'default' SA bound + to the exporter pods. type: string required: - - name + - registry type: object fileBundles: additionalProperties: @@ -70,32 +283,6 @@ spec: - rpi3 - rpi4 type: string - outputImage: - properties: - cloud: - default: other - enum: - - ecr - - other - type: string - dockerConfigSecretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - name - type: object - registry: - type: string - repository: - type: string - tag: - type: string - required: - - cloud - type: object type: object status: description: OSArtifactStatus defines the observed state of OSArtifact diff --git a/charts/osbuilder/templates/serviceaccount.yaml b/charts/osbuilder/templates/serviceaccount.yaml index c4fc9da..0ca8b84 100644 --- a/charts/osbuilder/templates/serviceaccount.yaml +++ b/charts/osbuilder/templates/serviceaccount.yaml @@ -1,5 +1,5 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: '{{ include "helm-chart.serviceAccountName" . }}' - namespace: '{{.Release.Namespace}}' + name: '{{ include "helm-chart.serviceAccountName" . }}' + namespace: '{{.Release.Namespace}}' diff --git a/config/crd/bases/build.kairos.io_osartifacts.yaml b/config/crd/bases/build.kairos.io_osartifacts.yaml index d5f1df6..367ea5e 100644 --- a/config/crd/bases/build.kairos.io_osartifacts.yaml +++ b/config/crd/bases/build.kairos.io_osartifacts.yaml @@ -48,13 +48,226 @@ spec: type: string type: array cloudConfigRef: + description: SecretKeySelector selects a key of a Secret. properties: key: + description: The key of the secret to select from. Must be a + valid secret key. type: string name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + exporter: + description: |- + Exporter when provided it will spawn an exporter job that + pushes images built by the osbuilder to the provided registry. + properties: + extraEnvVars: + description: ExtraEnvVars allows to append extra env vars to the + exporter pods. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + registry: + description: Registry is a registry spec used to push the final + images built by the osbuilder. + properties: + dockerConfigSecretKeyRef: + description: |- + DockerConfigSecretKeyRef is a reference to the secret that holds the `config.json` auth file. + It should be in a format that `docker login` can accept to auth to the registry. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + image: + description: Image defines the image details required to push + image to the registry. + properties: + repository: + description: Repository is the name of repository where + image is being pushed. + type: string + tag: + description: Tag is the tag name of the image being pushed. + Defaults to 'latest' if not provided. + type: string + required: + - repository + type: object + name: + description: Name is a DNS name of the registry. It has to + be accessible by the pod. + type: string + type: + default: other + description: "Type is a kind of registry being used. Currently + supported values are:\n\t- ecr \t- Amazon Elastic Container + Registry. Use only if a pod runs on\n\t\t\t an eks cluster + and has permissions to push to the registry.\n\t- other + - Any other type of the registry. It requires DockerConfigSecretKeyRef\n\t\t\t + \ to be provided in order to auth to the registry." + enum: + - ecr + - other + type: string + required: + - image + - name + - type + type: object + serviceAccount: + description: ServiceAccount allows overriding 'default' SA bound + to the exporter pods. type: string required: - - name + - registry type: object fileBundles: additionalProperties: @@ -70,32 +283,6 @@ spec: - rpi3 - rpi4 type: string - outputImage: - properties: - cloud: - default: other - enum: - - ecr - - other - type: string - dockerConfigSecretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - name - type: object - registry: - type: string - repository: - type: string - tag: - type: string - required: - - cloud - type: object type: object status: description: OSArtifactStatus defines the observed state of OSArtifact diff --git a/controllers/osartifact_controller.go b/controllers/osartifact_controller.go index 12505d7..e85b118 100644 --- a/controllers/osartifact_controller.go +++ b/controllers/osartifact_controller.go @@ -51,7 +51,7 @@ const ( artifactExporterIndexAnnotation = "build.kairos.io/export-index" ready = "Ready" ) -const threeHours = int32(10800) +const threeHours = int32(3 * 60 * 60) var ( requeue = ctrl.Result{RequeueAfter: requeueAfter} @@ -260,7 +260,7 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil return ctrl.Result{}, fmt.Errorf("failed to locate artifact pvc") } - if artifact.Spec.OutputImage != nil { + if artifact.Spec.Exporter != nil { idx := fmt.Sprintf("%d", 1) job := indexedJobs[idx] @@ -280,7 +280,8 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil BackoffLimit: ptr(int32(1)), Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ - RestartPolicy: corev1.RestartPolicyOnFailure, + ServiceAccountName: artifact.Spec.Exporter.ServiceAccountName(), + RestartPolicy: corev1.RestartPolicyNever, InitContainers: []corev1.Container{ { Name: "init-container", @@ -303,13 +304,18 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil }, } + tag := artifact.Spec.Exporter.Registry.Image.Tag + if len(tag) == 0 { + tag = "latest" + } + container := corev1.Container{ Name: "exporter", Image: "gcr.io/kaniko-project/executor:latest", Args: []string{ "--context=/artifacts/", "--dockerfile=/artifacts/Dockerfile", - fmt.Sprintf("--destination=%s/%s:%s", artifact.Spec.OutputImage.Registry, artifact.Spec.OutputImage.Repository, artifact.Spec.OutputImage.Tag), + fmt.Sprintf("--destination=%s/%s:%s", artifact.Spec.Exporter.Registry.Name, artifact.Spec.Exporter.Registry.Image.Repository, tag), }, VolumeMounts: []corev1.VolumeMount{ { @@ -318,19 +324,30 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil SubPath: "artifacts", }, }, + Env: []corev1.EnvVar{}, } - if artifact.Spec.OutputImage != nil && artifact.Spec.OutputImage.Cloud == osbuilder.RegistryCloudECR { - container.Env = []corev1.EnvVar{ + // Append EKS specific env vars if using ECR registry + if artifact.Spec.Exporter.IsECRRegistry() { + container.Env = append(container.Env, []corev1.EnvVar{ {Name: "AWS_SDK_LOAD_CONFIG", Value: "true"}, {Name: "AWS_EC2_METADATA_DISABLED", Value: "true"}, - } + }...) } - if artifact.Spec.OutputImage != nil && artifact.Spec.OutputImage.DockerConfigSecretKeyRef != nil { - if err := r.Get(ctx, client.ObjectKey{Namespace: artifact.Namespace, Name: artifact.Spec.OutputImage.DockerConfigSecretKeyRef.Name}, &corev1.Secret{}); err != nil { + // Append extra env vars + if artifact.Spec.Exporter.HasExtraEnvVars() { + container.Env = append(container.Env, *artifact.Spec.Exporter.ExtraEnvVars...) + } + + // Mount custom config.json file from secret + if artifact.Spec.Exporter.HasDockerConfigSecret() { + name := artifact.Spec.Exporter.Registry.DockerConfigSecretKeyRef.Name + key := artifact.Spec.Exporter.Registry.DockerConfigSecretKeyRef.Key + + if err := r.Get(ctx, client.ObjectKey{Namespace: artifact.Namespace, Name: name}, &corev1.Secret{}); err != nil { if errors.IsNotFound(err) { - logger.Info(fmt.Sprintf("Secret %s/%s not found", artifact.Namespace, artifact.Spec.OutputImage.DockerConfigSecretKeyRef.Name)) + logger.Info(fmt.Sprintf("Secret %s/%s not found", artifact.Namespace, name)) return requeue, nil } return ctrl.Result{}, err @@ -343,10 +360,10 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil Name: "docker-secret", VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: artifact.Spec.OutputImage.DockerConfigSecretKeyRef.Name, + SecretName: name, Items: []corev1.KeyToPath{{ - Key: artifact.Spec.OutputImage.DockerConfigSecretKeyRef.Key, - Path: artifact.Spec.OutputImage.DockerConfigSecretKeyRef.Key, + Key: key, + Path: "config.json", }}, }, },