diff --git a/api/crds/manifests/openmcp.cloud_clusterproviders.yaml b/api/crds/manifests/openmcp.cloud_clusterproviders.yaml index 3b97391..e480ac4 100644 --- a/api/crds/manifests/openmcp.cloud_clusterproviders.yaml +++ b/api/crds/manifests/openmcp.cloud_clusterproviders.yaml @@ -1942,6 +1942,22 @@ spec: - name type: object type: array + initCommand: + description: |- + InitCommand is the command that is executed to run the init job of the provider. + Defaults to ["init"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array + runCommand: + description: |- + RunCommand is the command that is executed to run the provider controllers. + Defaults to ["run"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array verbosity: default: INFO description: Verbosity is the verbosity level of the provider. diff --git a/api/crds/manifests/openmcp.cloud_platformservices.yaml b/api/crds/manifests/openmcp.cloud_platformservices.yaml index 9742a3a..947d57f 100644 --- a/api/crds/manifests/openmcp.cloud_platformservices.yaml +++ b/api/crds/manifests/openmcp.cloud_platformservices.yaml @@ -1942,6 +1942,22 @@ spec: - name type: object type: array + initCommand: + description: |- + InitCommand is the command that is executed to run the init job of the provider. + Defaults to ["init"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array + runCommand: + description: |- + RunCommand is the command that is executed to run the provider controllers. + Defaults to ["run"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array verbosity: default: INFO description: Verbosity is the verbosity level of the provider. diff --git a/api/crds/manifests/openmcp.cloud_serviceproviders.yaml b/api/crds/manifests/openmcp.cloud_serviceproviders.yaml index f3a2d87..11309e0 100644 --- a/api/crds/manifests/openmcp.cloud_serviceproviders.yaml +++ b/api/crds/manifests/openmcp.cloud_serviceproviders.yaml @@ -1942,6 +1942,22 @@ spec: - name type: object type: array + initCommand: + description: |- + InitCommand is the command that is executed to run the init job of the provider. + Defaults to ["init"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array + runCommand: + description: |- + RunCommand is the command that is executed to run the provider controllers. + Defaults to ["run"], if not specified. + The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + items: + type: string + type: array verbosity: default: INFO description: Verbosity is the verbosity level of the provider. diff --git a/api/provider/v1alpha1/deployment_types.go b/api/provider/v1alpha1/deployment_types.go index 09b4f76..9e9258a 100644 --- a/api/provider/v1alpha1/deployment_types.go +++ b/api/provider/v1alpha1/deployment_types.go @@ -21,9 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // DeploymentSpec defines the desired state of a provider. type DeploymentSpec struct { // Image is the name of the image of a provider. @@ -34,6 +31,18 @@ type DeploymentSpec struct { // They can be used to fetch provider images from private registries. ImagePullSecrets []ObjectReference `json:"imagePullSecrets,omitempty"` + // InitCommand is the command that is executed to run the init job of the provider. + // Defaults to ["init"], if not specified. + // The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + // +optional + InitCommand []string `json:"initCommand,omitempty"` + + // RunCommand is the command that is executed to run the provider controllers. + // Defaults to ["run"], if not specified. + // The '--environment', '--verbosity', and '--provider-name' flags will be appended to the command automatically. + // +optional + RunCommand []string `json:"runCommand,omitempty"` + // Env is a list of environment variables to set in the containers of the init job and deployment of the provider. // +optional // +patchMergeKey=name diff --git a/api/provider/v1alpha1/zz_generated.deepcopy.go b/api/provider/v1alpha1/zz_generated.deepcopy.go index 9325569..177cc09 100644 --- a/api/provider/v1alpha1/zz_generated.deepcopy.go +++ b/api/provider/v1alpha1/zz_generated.deepcopy.go @@ -109,6 +109,16 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { *out = make([]ObjectReference, len(*in)) copy(*out, *in) } + if in.InitCommand != nil { + in, out := &in.InitCommand, &out.InitCommand + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.RunCommand != nil { + in, out := &in.RunCommand, &out.RunCommand + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]EnvVar, len(*in)) diff --git a/internal/controllers/provider/controller_test.go b/internal/controllers/provider/controller_test.go index 51cc80c..08f1102 100644 --- a/internal/controllers/provider/controller_test.go +++ b/internal/controllers/provider/controller_test.go @@ -44,7 +44,7 @@ var _ = Describe("Deployment Controller", func() { return deploymentSpec, deploymentStatus } - reconcileProvider := func(env *testutils.Environment, req reconcile.Request, gvk schema.GroupVersionKind) { + reconcileProvider := func(env *testutils.Environment, req reconcile.Request, gvk schema.GroupVersionKind) *v1alpha1.DeploymentSpec { provider := &unstructured.Unstructured{} provider.SetGroupVersionKind(gvk) provider.SetName(req.Name) @@ -61,7 +61,11 @@ var _ = Describe("Deployment Controller", func() { job := install.NewJobMutator(values, deploymentSpec, nil).Empty() Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(job), job)).To(Succeed()) Expect(job.Spec.Template.Spec.Containers[0].Image).To(Equal("test-image:v0.1.0"), "Job container image should match the provider spec") - Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElement("init"), "Job container args should contain the init command") + if len(deploymentSpec.InitCommand) > 0 { + Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElements(deploymentSpec.InitCommand), "Job container args should contain the overwritten init command") + } else { + Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElement("init"), "Job container args should contain the init command") + } Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--environment=test-environment"), "Job container args should contain the environment") Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--verbosity=DEBUG"), "Job container args should contain the verbosity") Expect(job.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--provider-name="+req.Name), "Job container args should contain the provider name") @@ -80,7 +84,11 @@ var _ = Describe("Deployment Controller", func() { deploy := install.NewDeploymentMutator(values).Empty() Expect(env.Client().Get(env.Ctx, client.ObjectKeyFromObject(deploy), deploy)).To(Succeed()) Expect(deploy.Spec.Template.Spec.Containers[0].Image).To(Equal("test-image:v0.1.0"), "Deployment container image should match the provider spec") - Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElement("run"), "Deployment container args should contain the run command") + if len(deploymentSpec.RunCommand) > 0 { + Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElements(deploymentSpec.RunCommand), "Deployment container args should contain the overwritten run command") + } else { + Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElement("run"), "Deployment container args should contain the run command") + } Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--environment=test-environment"), "Deployment container args should contain the environment") Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--verbosity=DEBUG"), "Deployment container args should contain the verbosity") Expect(deploy.Spec.Template.Spec.Containers[0].Args).To(ContainElement("--provider-name="+req.Name), "Deployment container args should contain the provider name") @@ -102,6 +110,7 @@ var _ = Describe("Deployment Controller", func() { // delete the provider Expect(env.Client().Delete(env.Ctx, provider)).To(Succeed(), "Provider deletion should succeed") env.ShouldReconcile(req, "Reconcile after provider deletion should not return an error") + return deploymentSpec } It("should reconcile a cluster provider", func() { @@ -122,6 +131,14 @@ var _ = Describe("Deployment Controller", func() { reconcileProvider(env, req, v1alpha1.PlatformServiceGKV()) }) + It("should reconcile a platform service with overwritten init and run commands", func() { + env := buildTestEnvironment("test-04", v1alpha1.PlatformServiceGKV()) + req := testutils.RequestFromStrings("platform-service-test-04") + deploymentSpec := reconcileProvider(env, req, v1alpha1.PlatformServiceGKV()) + Expect(deploymentSpec.InitCommand).To(HaveLen(2)) + Expect(deploymentSpec.RunCommand).To(HaveLen(2)) + }) + }) Context("Converter", func() { diff --git a/internal/controllers/provider/install/deployment.go b/internal/controllers/provider/install/deployment.go index 4e220e9..96fd311 100644 --- a/internal/controllers/provider/install/deployment.go +++ b/internal/controllers/provider/install/deployment.go @@ -2,6 +2,7 @@ package install import ( "fmt" + "slices" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -54,6 +55,15 @@ func (m *deploymentMutator) Mutate(d *appsv1.Deployment) error { return err } + runCmd := slices.Clone(m.values.deploymentSpec.RunCommand) + if len(runCmd) == 0 { + runCmd = []string{"run"} + } + runCmd = append(runCmd, + "--environment="+m.values.Environment(), + "--verbosity="+m.values.Verbosity(), + "--provider-name="+m.values.provider.GetName(), + ) d.Spec = appsv1.DeploymentSpec{ Replicas: ptr.To[int32](1), Selector: &metav1.LabelSelector{ @@ -69,14 +79,9 @@ func (m *deploymentMutator) Mutate(d *appsv1.Deployment) error { Name: m.values.NamespacedDefaultResourceName(), Image: m.values.Image(), ImagePullPolicy: corev1.PullIfNotPresent, - Args: []string{ - "run", - "--environment=" + m.values.Environment(), - "--verbosity=" + m.values.Verbosity(), - "--provider-name=" + m.values.provider.GetName(), - }, - Env: env, - VolumeMounts: m.values.deploymentSpec.ExtraVolumeMounts, + Args: runCmd, + Env: env, + VolumeMounts: m.values.deploymentSpec.ExtraVolumeMounts, }, }, ImagePullSecrets: m.values.ImagePullSecrets(), diff --git a/internal/controllers/provider/install/job.go b/internal/controllers/provider/install/job.go index 61b7b4c..bc76f35 100644 --- a/internal/controllers/provider/install/job.go +++ b/internal/controllers/provider/install/job.go @@ -2,6 +2,7 @@ package install import ( "fmt" + "slices" "github.com/openmcp-project/controller-utils/pkg/resources" v1 "k8s.io/api/batch/v1" @@ -60,6 +61,15 @@ func (m *jobMutator) Mutate(j *v1.Job) error { return err } + initCmd := slices.Clone(m.values.deploymentSpec.InitCommand) + if len(initCmd) == 0 { + initCmd = []string{"init"} + } + initCmd = append(initCmd, + "--environment="+m.values.Environment(), + "--verbosity="+m.values.Verbosity(), + "--provider-name="+m.values.provider.GetName(), + ) j.Spec = v1.JobSpec{ BackoffLimit: ptr.To[int32](4), Template: corev1.PodTemplateSpec{ @@ -72,13 +82,8 @@ func (m *jobMutator) Mutate(j *v1.Job) error { Name: "init", Image: m.values.Image(), ImagePullPolicy: corev1.PullIfNotPresent, - Args: []string{ - "init", - "--environment=" + m.values.Environment(), - "--verbosity=" + m.values.Verbosity(), - "--provider-name=" + m.values.provider.GetName(), - }, - Env: env, + Args: initCmd, + Env: env, }, }, ServiceAccountName: m.values.NamespacedResourceName(initPrefix), diff --git a/internal/controllers/provider/testdata/test-04/platformservice.yaml b/internal/controllers/provider/testdata/test-04/platformservice.yaml new file mode 100644 index 0000000..6ebfb82 --- /dev/null +++ b/internal/controllers/provider/testdata/test-04/platformservice.yaml @@ -0,0 +1,18 @@ +apiVersion: openmcp.cloud/v1alpha1 +kind: PlatformService +metadata: + name: platform-service-test-04 + generation: 1 +spec: + image: "test-image:v0.1.0" + imagePullSecrets: [] + initCommand: + - test-init + - init-subcommand + runCommand: + - test-run + - run-subcommand + env: + - name: NAME + value: "test-name" + verbosity: DEBUG