diff --git a/api/v1beta1/params.go b/api/v1beta1/params.go new file mode 100644 index 0000000..f5659aa --- /dev/null +++ b/api/v1beta1/params.go @@ -0,0 +1,42 @@ +package v1beta1 + +import ( + "encoding/json" +) + +// Params represents untyped configuration. +// +kubebuilder:validation:Type=object +type Params struct { + // Data holds the parameter keys and values. + Data map[string]interface{} `json:"-"` +} + +// MarshalJSON implements the Marshaler interface. +func (c *Params) MarshalJSON() ([]byte, error) { + return json.Marshal(c.Data) +} + +// UnmarshalJSON implements the Unmarshaler interface. +func (c *Params) UnmarshalJSON(data []byte) error { + var out map[string]interface{} + err := json.Unmarshal(data, &out) + if err != nil { + return err + } + c.Data = out + return nil +} + +// DeepCopyInto is a deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (c *Params) DeepCopyInto(out *Params) { + bytes, err := json.Marshal(c.Data) + if err != nil { + panic(err) + } + var clone map[string]interface{} + err = json.Unmarshal(bytes, &clone) + if err != nil { + panic(err) + } + out.Data = clone +} diff --git a/api/v1beta1/tenant_types.go b/api/v1beta1/tenant_types.go index ab3d83e..e2191ff 100644 --- a/api/v1beta1/tenant_types.go +++ b/api/v1beta1/tenant_types.go @@ -25,8 +25,9 @@ type TenantSpec struct { ControllerName string `json:"controllerName,omitempty"` // ExtraParams is a map of extra parameters that can be used in the templates. + // +kubebuilder:pruning:PreserveUnknownFields // +optional - ExtraParams map[string]string `json:"extraParams,omitempty"` + ExtraParams Params `json:"extraParams,omitempty"` } // RootNamespaceSpec defines the desired state of Namespace. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 645dbe5..be49fdd 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -49,6 +49,16 @@ func (in *DelegateSpec) DeepCopy() *DelegateSpec { return out } +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Params. +func (in *Params) DeepCopy() *Params { + if in == nil { + return nil + } + out := new(Params) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RootNamespaceSpec) DeepCopyInto(out *RootNamespaceSpec) { *out = *in @@ -155,13 +165,7 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.ExtraParams != nil { - in, out := &in.ExtraParams, &out.ExtraParams - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } + in.ExtraParams.DeepCopyInto(&out.ExtraParams) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantSpec. diff --git a/charts/cattage/crds/tenant.yaml b/charts/cattage/crds/tenant.yaml index f4bd0b4..e12779d 100644 --- a/charts/cattage/crds/tenant.yaml +++ b/charts/cattage/crds/tenant.yaml @@ -78,10 +78,9 @@ spec: type: object type: array extraParams: - additionalProperties: - type: string description: ExtraParams is a map of extra parameters that can be used in the templates. type: object + x-kubernetes-preserve-unknown-fields: true rootNamespaces: description: RootNamespaces are the list of root namespaces that belong to this tenant. items: diff --git a/config/crd/bases/cattage.cybozu.io_tenants.yaml b/config/crd/bases/cattage.cybozu.io_tenants.yaml index 8760070..2576399 100644 --- a/config/crd/bases/cattage.cybozu.io_tenants.yaml +++ b/config/crd/bases/cattage.cybozu.io_tenants.yaml @@ -80,11 +80,10 @@ spec: type: object type: array extraParams: - additionalProperties: - type: string description: ExtraParams is a map of extra parameters that can be used in the templates. type: object + x-kubernetes-preserve-unknown-fields: true rootNamespaces: description: RootNamespaces are the list of root namespaces that belong to this tenant. diff --git a/config/manager/configmap.yaml b/config/manager/configmap.yaml index 9155862..ddb8308 100644 --- a/config/manager/configmap.yaml +++ b/config/manager/configmap.yaml @@ -41,6 +41,10 @@ data: - namespace: {{ . }} server: '*' {{- end }} + {{- range .ExtraParams.Destinations }} + - namespace: {{ . }} + server: '*' + {{- end }} namespaceResourceBlacklist: - group: "" kind: ResourceQuota diff --git a/config/samples/tenant.yaml b/config/samples/tenant.yaml index 397deef..f147995 100644 --- a/config/samples/tenant.yaml +++ b/config/samples/tenant.yaml @@ -24,4 +24,6 @@ spec: roles: - admin extraParams: - GitHubTeam: b-team-gh + Destinations: + - "extra-namespace-x" + - "extra-namespace-y" diff --git a/controllers/tenant_controller.go b/controllers/tenant_controller.go index e0d961e..6f1fcb7 100644 --- a/controllers/tenant_controller.go +++ b/controllers/tenant_controller.go @@ -371,7 +371,7 @@ func (r *TenantReconciler) rolesMap(ctx context.Context, delegates []cattagev1be } result[role] = append(result[role], Role{ Name: delegatedTenant.Name, - ExtraParams: delegatedTenant.Spec.ExtraParams, + ExtraParams: delegatedTenant.Spec.ExtraParams.Data, }) } } @@ -417,11 +417,11 @@ func (r *TenantReconciler) reconcileNamespaces(ctx context.Context, tenant *catt err = tpl.Execute(&buf, struct { Name string Roles map[string][]Role - ExtraParams map[string]string + ExtraParams map[string]interface{} }{ Name: tenant.Name, Roles: roles, - ExtraParams: tenant.Spec.ExtraParams, + ExtraParams: tenant.Spec.ExtraParams.Data, }) if err != nil { return err @@ -467,7 +467,7 @@ func (r *TenantReconciler) reconcileNamespaces(ctx context.Context, tenant *catt type Role struct { Name string - ExtraParams map[string]string + ExtraParams map[string]interface{} } func (r *TenantReconciler) reconcileArgoCD(ctx context.Context, tenant *cattagev1beta1.Tenant) error { @@ -505,13 +505,13 @@ func (r *TenantReconciler) reconcileArgoCD(ctx context.Context, tenant *cattagev Namespaces []string Roles map[string][]Role Repositories []string - ExtraParams map[string]string + ExtraParams map[string]interface{} }{ Name: tenant.Name, Namespaces: namespaces, Roles: roles, Repositories: tenant.Spec.ArgoCD.Repositories, - ExtraParams: tenant.Spec.ExtraParams, + ExtraParams: tenant.Spec.ExtraParams.Data, }) if err != nil { return err diff --git a/controllers/tenant_controller_test.go b/controllers/tenant_controller_test.go index 93dc761..e82bfdb 100644 --- a/controllers/tenant_controller_test.go +++ b/controllers/tenant_controller_test.go @@ -107,9 +107,9 @@ var _ = Describe("Tenant controller", Ordered, func() { "https://github.com/cybozu-go/*", }, }, - ExtraParams: map[string]string{ + ExtraParams: cattagev1beta1.Params{Data: map[string]interface{}{ "GitHubTeam": "c-team-gh", - }, + }}, }, } err := k8sClient.Create(ctx, cTeam) @@ -143,9 +143,13 @@ var _ = Describe("Tenant controller", Ordered, func() { }, }, }, - ExtraParams: map[string]string{ + ExtraParams: cattagev1beta1.Params{Data: map[string]interface{}{ "GitHubTeam": "x-team-gh", - }, + "Destinations": []string{ + "extra-namespace-x", + "extra-namespace-y", + }, + }}, }, } err = k8sClient.Create(ctx, xTeam) @@ -209,6 +213,14 @@ var _ = Describe("Tenant controller", Ordered, func() { "namespace": Equal("sub-4"), "server": Equal("*"), }), + MatchAllKeys(Keys{ + "namespace": Equal("extra-namespace-x"), + "server": Equal("*"), + }), + MatchAllKeys(Keys{ + "namespace": Equal("extra-namespace-y"), + "server": Equal("*"), + }), ), "namespaceResourceBlacklist": ConsistOf( MatchAllKeys(Keys{