From 49fa8d377d9a0f91618da9ca0991ad2af3d63ddd Mon Sep 17 00:00:00 2001 From: Johannes Aubart Date: Mon, 14 Nov 2022 12:32:14 +0100 Subject: [PATCH] resolve secret references in targets in deployer lib (#629) * add resolver interface and secret resolver implementation * change deployer definition to use resolved targets instead * refactor target types into own package * fix kubeconfig loading for helm and manifest deployers * add check for different namespace * make target available in container * fix tests * fix kubeconfig decoding * fix integration tests * increase timeouts for integration tests (run-int-tests) * fix target mount in container deployer * move ResolvedTarget definition to apis and make Target.Spec.Configuration a pointer (run-int-tests) * add documentation for container deployitems and targets * validate targets * use LocalSecretReference instead of SecretReference for Targets * fix nil pointer * rebase fix * fix webhook integration test (run-int-tests) * copy target to shared volume instead of mounting * add integration tests for target propagation in container deployitems * improve docs --- .ci/int-test-helper/install-landscaper | 4 +- apis/core/types_shared.go | 6 + apis/core/types_target.go | 15 +- .../targettypes/kubernetes_cluster.go | 94 ++++++++++ apis/core/v1alpha1/types_shared.go | 6 + apis/core/v1alpha1/types_target.go | 80 ++------- apis/core/v1alpha1/zz_generated.conversion.go | 44 ++++- apis/core/v1alpha1/zz_generated.deepcopy.go | 72 +++----- apis/core/validation/target.go | 28 +++ apis/core/validation/target_test.go | 71 ++++++++ apis/core/zz_generated.deepcopy.go | 29 ++- apis/deployer/container/constants.go | 16 ++ apis/openapi/openapi_generated.go | 97 ++++------ cmd/landscaper-webhooks-server/app/options.go | 5 + .../landscaper/apis/core/types_shared.go | 6 + .../landscaper/apis/core/types_target.go | 15 +- .../apis/core/v1alpha1/types_shared.go | 6 + .../apis/core/v1alpha1/types_target.go | 80 ++------- .../core/v1alpha1/zz_generated.conversion.go | 44 ++++- .../core/v1alpha1/zz_generated.deepcopy.go | 72 +++----- .../apis/core/zz_generated.deepcopy.go | 29 ++- docs/api-reference/core.md | 128 ++++++-------- docs/deployer/container.md | 60 ++++++- docs/technical/target_types.md | 21 +-- docs/usage/Targets.md | 145 +++++++++++++++ ...install-landscaper-for-integration-test.sh | 4 +- pkg/agent/agent.go | 14 +- pkg/deployer/container/container.go | 5 +- pkg/deployer/container/container_reconcile.go | 28 +++ pkg/deployer/container/init/e2e_test.go | 4 + pkg/deployer/container/init/init.go | 16 ++ .../container/init/init_suite_test.go | 80 ++++----- pkg/deployer/container/init/options.go | 5 + pkg/deployer/container/pod.go | 24 ++- .../container/reconciler_deployitem.go | 12 +- pkg/deployer/helm/deployer.go | 10 +- pkg/deployer/helm/helm.go | 36 ++-- pkg/deployer/helm/test/e2e_test.go | 7 +- pkg/deployer/lib/controller.go | 34 +++- .../secret/secretrefresolver.go | 49 ++++++ .../lib/targetresolver/targetresolver.go | 28 +++ pkg/deployer/lib/utils.go | 5 +- pkg/deployer/manifest/controller.go | 10 +- pkg/deployer/manifest/export_test.go | 5 +- pkg/deployer/manifest/manifest.go | 36 ++-- pkg/deployer/manifest/misc_test.go | 95 ++++------ pkg/deployer/manifest/policy_test.go | 38 ++-- pkg/deployer/manifest/readiness_check_test.go | 8 +- pkg/deployer/mock/controller.go | 6 +- .../targetsync/targetsync_controller.go | 12 +- .../targetsync/targetsync_controller_test.go | 4 +- ...andscaper.gardener.cloud_environments.yaml | 5 +- .../landscaper.gardener.cloud_targets.yaml | 5 +- pkg/landscaper/dataobjects/target.go | 22 ++- .../installations/exports/constructor.go | 2 +- .../installations/exports/constructor_test.go | 4 +- pkg/landscaper/installations/helper.go | 131 +------------- .../installations/operation_test.go | 8 +- pkg/utils/builders.go | 6 +- .../landscaper/installation_simulator_test.go | 7 +- pkg/utils/landscaper/landscaper.go | 9 +- pkg/utils/references.go | 137 +++++++++++++++ pkg/utils/utils_suite_test.go | 93 ++++++++++ pkg/utils/webhook/webhook.go | 46 ++++- .../deployers/container/containertests.go | 166 ++++++++++++++++++ test/integration/deployitems/timeouts.go | 6 +- test/integration/suite_test.go | 2 +- test/integration/targets/targets.go | 9 +- test/integration/webhook/webhook.go | 21 +++ test/utils/resources.go | 34 ++-- .../landscaper/apis/core/types_shared.go | 6 + .../landscaper/apis/core/types_target.go | 15 +- .../targettypes/kubernetes_cluster.go | 94 ++++++++++ .../apis/core/v1alpha1/types_shared.go | 6 + .../apis/core/v1alpha1/types_target.go | 80 ++------- .../core/v1alpha1/zz_generated.conversion.go | 44 ++++- .../core/v1alpha1/zz_generated.deepcopy.go | 72 +++----- .../landscaper/apis/core/validation/target.go | 28 +++ .../apis/core/zz_generated.deepcopy.go | 29 ++- .../apis/deployer/container/constants.go | 16 ++ vendor/modules.txt | 1 + 81 files changed, 1901 insertions(+), 951 deletions(-) create mode 100644 apis/core/v1alpha1/targettypes/kubernetes_cluster.go create mode 100644 apis/core/validation/target.go create mode 100644 apis/core/validation/target_test.go create mode 100644 pkg/deployer/lib/targetresolver/secret/secretrefresolver.go create mode 100644 pkg/deployer/lib/targetresolver/targetresolver.go create mode 100644 pkg/utils/references.go create mode 100644 vendor/github.com/gardener/landscaper/apis/core/v1alpha1/targettypes/kubernetes_cluster.go create mode 100644 vendor/github.com/gardener/landscaper/apis/core/validation/target.go diff --git a/.ci/int-test-helper/install-landscaper b/.ci/int-test-helper/install-landscaper index 492503c02..c68cafabb 100755 --- a/.ci/int-test-helper/install-landscaper +++ b/.ci/int-test-helper/install-landscaper @@ -32,8 +32,8 @@ landscaper: - manifest - mock deployItemTimeouts: - pickup: 10s - abort: 10s + pickup: 30s + abort: 30s " > $TMP/values.yaml touch $TMP/registry-values.yaml diff --git a/apis/core/types_shared.go b/apis/core/types_shared.go index e28395740..e941efe66 100644 --- a/apis/core/types_shared.go +++ b/apis/core/types_shared.go @@ -85,6 +85,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/apis/core/types_target.go b/apis/core/types_target.go index b42bc7016..5756f912f 100644 --- a/apis/core/types_target.go +++ b/apis/core/types_target.go @@ -41,12 +41,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -68,3 +68,14 @@ type TargetTemplate struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` } + +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` + + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` +} diff --git a/apis/core/v1alpha1/targettypes/kubernetes_cluster.go b/apis/core/v1alpha1/targettypes/kubernetes_cluster.go new file mode 100644 index 000000000..d4fcae034 --- /dev/null +++ b/apis/core/v1alpha1/targettypes/kubernetes_cluster.go @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package targettypes + +import ( + "encoding/json" + + "k8s.io/utils/pointer" + + "github.com/gardener/landscaper/apis/core" + "github.com/gardener/landscaper/apis/core/v1alpha1" +) + +// KubernetesClusterTargetType defines the landscaper kubernetes cluster target. +const KubernetesClusterTargetType v1alpha1.TargetType = core.GroupName + "/kubernetes-cluster" + +// KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config. +type KubernetesClusterTargetConfig struct { + // Kubeconfig defines kubeconfig as string. + Kubeconfig ValueRef `json:"kubeconfig"` +} + +// DefaultKubeconfigKey is the default that is used to hold a kubeconfig. +const DefaultKubeconfigKey = "kubeconfig" + +// ValueRef holds a value that can be either defined by string or by a secret ref. +type ValueRef struct { + StrVal *string `json:"-"` + + // deprecated + SecretRef *v1alpha1.SecretReference `json:"secretRef,omitempty"` +} + +// kubeconfigJSON is a helper struct for decoding. +type kubeconfigJSON struct { + Kubeconfig *ValueRef `json:"kubeconfig"` +} + +// valueRefJSON is a helper struct to decode json into a secret ref object. +type valueRefJSON struct { + SecretRef *v1alpha1.SecretReference `json:"secretRef,omitempty"` +} + +// MarshalJSON implements the json marshaling for a JSON +func (v ValueRef) MarshalJSON() ([]byte, error) { + if v.StrVal != nil { + return json.Marshal(v.StrVal) + } + ref := valueRefJSON{ + SecretRef: v.SecretRef, + } + return json.Marshal(ref) +} + +// UnmarshalJSON implements json unmarshaling for a JSON +func (v *ValueRef) UnmarshalJSON(data []byte) error { + ref := &valueRefJSON{} + err := json.Unmarshal(data, ref) + if err == nil && ref.SecretRef != nil { + // parsing into secret reference was successful + v.SecretRef = ref.SecretRef + return nil + } + // parse into string instead + var strVal string + err = json.Unmarshal(data, &strVal) + if err == nil { + v.StrVal = &strVal + return nil + } + v.StrVal = pointer.String(string(data)) + return nil +} + +func (kc *KubernetesClusterTargetConfig) UnmarshalJSON(data []byte) error { + kj := &kubeconfigJSON{} + err := json.Unmarshal(data, kj) + if err == nil && kj.Kubeconfig != nil { + // parsing was successful + kc.Kubeconfig = *kj.Kubeconfig + return nil + } + return kc.Kubeconfig.UnmarshalJSON(data) +} + +func (v ValueRef) OpenAPISchemaType() []string { + return []string{ + "object", + "string", + } +} +func (v ValueRef) OpenAPISchemaFormat() string { return "" } diff --git a/apis/core/v1alpha1/types_shared.go b/apis/core/v1alpha1/types_shared.go index 7f939053c..b98779ad1 100644 --- a/apis/core/v1alpha1/types_shared.go +++ b/apis/core/v1alpha1/types_shared.go @@ -87,6 +87,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/apis/core/v1alpha1/types_target.go b/apis/core/v1alpha1/types_target.go index 49652852b..4f9d0261c 100644 --- a/apis/core/v1alpha1/types_target.go +++ b/apis/core/v1alpha1/types_target.go @@ -5,13 +5,9 @@ package v1alpha1 import ( - "encoding/json" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lsschema "github.com/gardener/landscaper/apis/schema" - - "github.com/gardener/landscaper/apis/core" ) // TargetType defines the type of the target. @@ -86,12 +82,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -114,69 +110,13 @@ type TargetTemplate struct { Annotations map[string]string `json:"annotations,omitempty"` } -////////////////////////////// -// Target Types // -////////////////////////////// -// todo: refactor to own package - -// KubernetesClusterTargetType defines the landscaper kubernetes cluster target. -const KubernetesClusterTargetType TargetType = core.GroupName + "/kubernetes-cluster" - -// KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config. -type KubernetesClusterTargetConfig struct { - // Kubeconfig defines kubeconfig as string. - Kubeconfig ValueRef `json:"kubeconfig"` -} - -// DefaultKubeconfigKey is the default that is used to hold a kubeconfig. -const DefaultKubeconfigKey = "kubeconfig" - -// ValueRef holds a value that can be either defined by string or by a secret ref. -type ValueRef struct { - StrVal *string `json:"-"` - - // deprecated - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// valueRefJSON is a helper struct to decode json into a secret ref object. -type valueRefJSON struct { - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// MarshalJSON implements the json marshaling for a JSON -func (v ValueRef) MarshalJSON() ([]byte, error) { - if v.StrVal != nil { - return json.Marshal(v.StrVal) - } - ref := valueRefJSON{ - SecretRef: v.SecretRef, - } - return json.Marshal(ref) -} - -// UnmarshalJSON implements json unmarshaling for a JSON -func (v *ValueRef) UnmarshalJSON(data []byte) error { - if data[0] == '"' { - var strVal string - if err := json.Unmarshal(data, &strVal); err != nil { - return err - } - v.StrVal = &strVal - return nil - } - ref := &valueRefJSON{} - if err := json.Unmarshal(data, ref); err != nil { - return err - } - v.SecretRef = ref.SecretRef - return nil -} +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` -func (v ValueRef) OpenAPISchemaType() []string { - return []string{ - "object", - "string", - } + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` } -func (v ValueRef) OpenAPISchemaFormat() string { return "" } diff --git a/apis/core/v1alpha1/zz_generated.conversion.go b/apis/core/v1alpha1/zz_generated.conversion.go index 1bc724bad..843e08c09 100644 --- a/apis/core/v1alpha1/zz_generated.conversion.go +++ b/apis/core/v1alpha1/zz_generated.conversion.go @@ -652,6 +652,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ResolvedTarget)(nil), (*core.ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(a.(*ResolvedTarget), b.(*core.ResolvedTarget), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*core.ResolvedTarget)(nil), (*ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(a.(*core.ResolvedTarget), b.(*ResolvedTarget), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*ResourceReference)(nil), (*core.ResourceReference)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ResourceReference_To_core_ResourceReference(a.(*ResourceReference), b.(*core.ResourceReference), scope) }); err != nil { @@ -2633,6 +2643,28 @@ func Convert_core_Requirement_To_v1alpha1_Requirement(in *core.Requirement, out return autoConvert_core_Requirement_To_v1alpha1_Requirement(in, out, s) } +func autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + out.Target = (*core.Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget is an autogenerated conversion function. +func Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + return autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in, out, s) +} + +func autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + out.Target = (*Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget is an autogenerated conversion function. +func Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + return autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in, out, s) +} + func autoConvert_v1alpha1_ResourceReference_To_core_ResourceReference(in *ResourceReference, out *core.ResourceReference, s conversion.Scope) error { out.ComponentName = in.ComponentName out.ResourceName = in.ResourceName @@ -2945,10 +2977,8 @@ func Convert_core_TargetSelector_To_v1alpha1_TargetSelector(in *core.TargetSelec func autoConvert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.TargetSpec, s conversion.Scope) error { out.Type = core.TargetType(in.Type) - if err := Convert_v1alpha1_AnyJSON_To_core_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*core.SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*core.AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*core.LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -2959,10 +2989,8 @@ func Convert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.Ta func autoConvert_core_TargetSpec_To_v1alpha1_TargetSpec(in *core.TargetSpec, out *TargetSpec, s conversion.Scope) error { out.Type = TargetType(in.Type) - if err := Convert_core_AnyJSON_To_v1alpha1_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } diff --git a/apis/core/v1alpha1/zz_generated.deepcopy.go b/apis/core/v1alpha1/zz_generated.deepcopy.go index 18f570677..fdb8c0b5a 100644 --- a/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -1724,23 +1724,6 @@ func (in *JSONSchemaDefinition) DeepCopy() *JSONSchemaDefinition { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesClusterTargetConfig) DeepCopyInto(out *KubernetesClusterTargetConfig) { - *out = *in - in.Kubeconfig.DeepCopyInto(&out.Kubeconfig) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesClusterTargetConfig. -func (in *KubernetesClusterTargetConfig) DeepCopy() *KubernetesClusterTargetConfig { - if in == nil { - return nil - } - out := new(KubernetesClusterTargetConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LocalSecretReference) DeepCopyInto(out *LocalSecretReference) { *out = *in @@ -1887,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2216,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return @@ -2428,32 +2436,6 @@ func (in *TypedObjectReference) DeepCopy() *TypedObjectReference { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ValueRef) DeepCopyInto(out *ValueRef) { - *out = *in - if in.StrVal != nil { - in, out := &in.StrVal, &out.StrVal - *out = new(string) - **out = **in - } - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValueRef. -func (in *ValueRef) DeepCopy() *ValueRef { - if in == nil { - return nil - } - out := new(ValueRef) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VersionedNamedObjectReference) DeepCopyInto(out *VersionedNamedObjectReference) { *out = *in diff --git a/apis/core/validation/target.go b/apis/core/validation/target.go new file mode 100644 index 000000000..8c866e3ed --- /dev/null +++ b/apis/core/validation/target.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package validation + +import ( + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/gardener/landscaper/apis/core" +) + +// ValidateTarget validates a Target +func ValidateTarget(target *core.Target) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, ValidateTargetSpec(&target.Spec, field.NewPath("spec"))...) + return allErrs +} + +func ValidateTargetSpec(spec *core.TargetSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if spec.Configuration != nil && spec.SecretRef != nil { + allErrs = append(allErrs, field.Invalid(fldPath, spec, "either config or secretRef may be set, not both")) + } + + return allErrs +} diff --git a/apis/core/validation/target_test.go b/apis/core/validation/target_test.go new file mode 100644 index 000000000..6fec1c7dd --- /dev/null +++ b/apis/core/validation/target_test.go @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package validation_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gstruct" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/gardener/landscaper/apis/core" + "github.com/gardener/landscaper/apis/core/validation" +) + +var _ = Describe("Target", func() { + Context("Spec", func() { + + It("should accept a Target with an empty spec", func() { + t := &core.Target{ + Spec: core.TargetSpec{}, + } + + allErrs := validation.ValidateTarget(t) + Expect(allErrs).To(BeEmpty()) + }) + + It("should reject a Target with secretRef and config set", func() { + t := &core.Target{ + Spec: core.TargetSpec{ + Configuration: core.NewAnyJSONPointer([]byte("foo")), + SecretRef: &core.LocalSecretReference{ + Name: "foo", + }, + }, + } + + allErrs := validation.ValidateTarget(t) + Expect(allErrs).To(ContainElement(PointTo(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("spec"), + })))) + }) + + It("should accept a Target with a secretRef", func() { + t := &core.Target{ + Spec: core.TargetSpec{ + SecretRef: &core.LocalSecretReference{ + Name: "foo", + }, + }, + } + + allErrs := validation.ValidateTarget(t) + Expect(allErrs).To(BeEmpty()) + }) + + It("should accept a Target with an inline config", func() { + t := &core.Target{ + Spec: core.TargetSpec{ + Configuration: core.NewAnyJSONPointer([]byte("foo")), + }, + } + + allErrs := validation.ValidateTarget(t) + Expect(allErrs).To(BeEmpty()) + }) + + }) +}) diff --git a/apis/core/zz_generated.deepcopy.go b/apis/core/zz_generated.deepcopy.go index d672d9267..0e3540f13 100644 --- a/apis/core/zz_generated.deepcopy.go +++ b/apis/core/zz_generated.deepcopy.go @@ -1870,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2199,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return diff --git a/apis/deployer/container/constants.go b/apis/deployer/container/constants.go index c3fd42af5..abdf8f3cb 100644 --- a/apis/deployer/container/constants.go +++ b/apis/deployer/container/constants.go @@ -80,6 +80,18 @@ const ComponentDescriptorPathName = "COMPONENT_DESCRIPTOR_PATH" // ComponentDescriptorPath is the path to the component descriptor file. var ComponentDescriptorPath = filepath.Join(SharedBasePath, "component_descriptor.json") +// TargetPathName is the name of the env var that points to the target content. +const TargetPathName = "TARGET_PATH" + +// TargetFileName is the name of the file that contains the target content. +const TargetFileName = "target.json" + +// TargetPath is the path to the target content file. +var TargetPath = filepath.Join(SharedBasePath, "targets", TargetFileName) + +// TargetInitDir is the directory in which the target is mounted in the init container, which then copies it to TargetPath. +var TargetInitDir = filepath.Join(BasePath, "targets") + // ContentPathName is the name of the env var that points to the blob content of the definition. const ContentPathName = "CONTENT_PATH" @@ -150,6 +162,10 @@ var ( Name: ComponentDescriptorPathName, Value: ComponentDescriptorPath, }, + { + Name: TargetPathName, + Value: TargetPath, + }, { Name: ContentPathName, Value: ContentPath, diff --git a/apis/openapi/openapi_generated.go b/apis/openapi/openapi_generated.go index 8d17a7645..36d069dee 100644 --- a/apis/openapi/openapi_generated.go +++ b/apis/openapi/openapi_generated.go @@ -133,7 +133,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/gardener/landscaper/apis/core/v1alpha1.InstallationTemplate": schema_landscaper_apis_core_v1alpha1_InstallationTemplate(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.InstallationTemplateBlueprintDefinition": schema_landscaper_apis_core_v1alpha1_InstallationTemplateBlueprintDefinition(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.JSONSchemaDefinition": schema_landscaper_apis_core_v1alpha1_JSONSchemaDefinition(ref), - "github.com/gardener/landscaper/apis/core/v1alpha1.KubernetesClusterTargetConfig": schema_landscaper_apis_core_v1alpha1_KubernetesClusterTargetConfig(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.LocalSecretReference": schema_landscaper_apis_core_v1alpha1_LocalSecretReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.LsHealthCheck": schema_landscaper_apis_core_v1alpha1_LsHealthCheck(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.LsHealthCheckList": schema_landscaper_apis_core_v1alpha1_LsHealthCheckList(ref), @@ -141,6 +140,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/gardener/landscaper/apis/core/v1alpha1.ObjectReference": schema_landscaper_apis_core_v1alpha1_ObjectReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.RemoteBlueprintReference": schema_landscaper_apis_core_v1alpha1_RemoteBlueprintReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.Requirement": schema_landscaper_apis_core_v1alpha1_Requirement(ref), + "github.com/gardener/landscaper/apis/core/v1alpha1.ResolvedTarget": schema_landscaper_apis_core_v1alpha1_ResolvedTarget(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.ResourceReference": schema_landscaper_apis_core_v1alpha1_ResourceReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.SecretLabelSelectorRef": schema_landscaper_apis_core_v1alpha1_SecretLabelSelectorRef(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference": schema_landscaper_apis_core_v1alpha1_SecretReference(ref), @@ -163,11 +163,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/gardener/landscaper/apis/core/v1alpha1.TemplateExecutor": schema_landscaper_apis_core_v1alpha1_TemplateExecutor(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.TokenRotation": schema_landscaper_apis_core_v1alpha1_TokenRotation(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.TypedObjectReference": schema_landscaper_apis_core_v1alpha1_TypedObjectReference(ref), - "github.com/gardener/landscaper/apis/core/v1alpha1.ValueRef": schema_landscaper_apis_core_v1alpha1_ValueRef(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.VersionedNamedObjectReference": schema_landscaper_apis_core_v1alpha1_VersionedNamedObjectReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.VersionedObjectReference": schema_landscaper_apis_core_v1alpha1_VersionedObjectReference(ref), "github.com/gardener/landscaper/apis/core/v1alpha1.VersionedResourceReference": schema_landscaper_apis_core_v1alpha1_VersionedResourceReference(ref), - "github.com/gardener/landscaper/apis/core/v1alpha1.valueRefJSON": schema_landscaper_apis_core_v1alpha1_valueRefJSON(ref), "github.com/gardener/landscaper/apis/deployer/container/v1alpha1.Configuration": schema_apis_deployer_container_v1alpha1_Configuration(ref), "github.com/gardener/landscaper/apis/deployer/container/v1alpha1.ContainerSpec": schema_apis_deployer_container_v1alpha1_ContainerSpec(ref), "github.com/gardener/landscaper/apis/deployer/container/v1alpha1.ContainerStatus": schema_apis_deployer_container_v1alpha1_ContainerStatus(ref), @@ -5657,29 +5655,6 @@ func schema_landscaper_apis_core_v1alpha1_JSONSchemaDefinition(ref common.Refere } } -func schema_landscaper_apis_core_v1alpha1_KubernetesClusterTargetConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kubeconfig": { - SchemaProps: spec.SchemaProps{ - Description: "Kubeconfig defines kubeconfig as string.", - Default: map[string]interface{}{}, - Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.ValueRef"), - }, - }, - }, - Required: []string{"kubeconfig"}, - }, - }, - Dependencies: []string{ - "github.com/gardener/landscaper/apis/core/v1alpha1.ValueRef"}, - } -} - func schema_landscaper_apis_core_v1alpha1_LocalSecretReference(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -5944,6 +5919,36 @@ func schema_landscaper_apis_core_v1alpha1_Requirement(ref common.ReferenceCallba } } +func schema_landscaper_apis_core_v1alpha1_ResolvedTarget(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "target": { + SchemaProps: spec.SchemaProps{ + Description: "Target contains the original target.", + Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.Target"), + }, + }, + "content": { + SchemaProps: spec.SchemaProps{ + Description: "Content contains the content of the target. If the target has a secret reference, this field should be filled by a TargetResolver. Otherwise, the inline configuration of the target is put here.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"target", "content"}, + }, + }, + Dependencies: []string{ + "github.com/gardener/landscaper/apis/core/v1alpha1.Target"}, + } +} + func schema_landscaper_apis_core_v1alpha1_ResourceReference(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -6535,7 +6540,7 @@ func schema_landscaper_apis_core_v1alpha1_TargetSpec(ref common.ReferenceCallbac "secretRef": { SchemaProps: spec.SchemaProps{ Description: "Reference to a secret containing the target type specific configuration. Exactly one of the fields Configuration and SecretRef must be set", - Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"), + Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.LocalSecretReference"), }, }, }, @@ -6543,7 +6548,7 @@ func schema_landscaper_apis_core_v1alpha1_TargetSpec(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/gardener/landscaper/apis/core/v1alpha1.AnyJSON", "github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"}, + "github.com/gardener/landscaper/apis/core/v1alpha1.AnyJSON", "github.com/gardener/landscaper/apis/core/v1alpha1.LocalSecretReference"}, } } @@ -6765,7 +6770,7 @@ func schema_landscaper_apis_core_v1alpha1_TargetTemplate(ref common.ReferenceCal "secretRef": { SchemaProps: spec.SchemaProps{ Description: "Reference to a secret containing the target type specific configuration. Exactly one of the fields Configuration and SecretRef must be set", - Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"), + Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.LocalSecretReference"), }, }, "labels": { @@ -6805,7 +6810,7 @@ func schema_landscaper_apis_core_v1alpha1_TargetTemplate(ref common.ReferenceCal }, }, Dependencies: []string{ - "github.com/gardener/landscaper/apis/core/v1alpha1.AnyJSON", "github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"}, + "github.com/gardener/landscaper/apis/core/v1alpha1.AnyJSON", "github.com/gardener/landscaper/apis/core/v1alpha1.LocalSecretReference"}, } } @@ -6919,18 +6924,6 @@ func schema_landscaper_apis_core_v1alpha1_TypedObjectReference(ref common.Refere } } -func schema_landscaper_apis_core_v1alpha1_ValueRef(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ValueRef holds a value that can be either defined by string or by a secret ref.", - Type: v1alpha1.ValueRef{}.OpenAPISchemaType(), - Format: v1alpha1.ValueRef{}.OpenAPISchemaFormat(), - }, - }, - } -} - func schema_landscaper_apis_core_v1alpha1_VersionedNamedObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -7038,26 +7031,6 @@ func schema_landscaper_apis_core_v1alpha1_VersionedResourceReference(ref common. } } -func schema_landscaper_apis_core_v1alpha1_valueRefJSON(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "valueRefJSON is a helper struct to decode json into a secret ref object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "secretRef": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/gardener/landscaper/apis/core/v1alpha1.SecretReference"}, - } -} - func schema_apis_deployer_container_v1alpha1_Configuration(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/cmd/landscaper-webhooks-server/app/options.go b/cmd/landscaper-webhooks-server/app/options.go index f1b252aa2..15d2717e2 100644 --- a/cmd/landscaper-webhooks-server/app/options.go +++ b/cmd/landscaper-webhooks-server/app/options.go @@ -33,6 +33,11 @@ func defaultWebhookedResources() map[string]webhook.WebhookedResourceDefinition APIVersions: []string{"v1alpha1"}, ResourceName: "executions", }, + "targets": { + APIGroup: "landscaper.gardener.cloud", + APIVersions: []string{"v1alpha1"}, + ResourceName: "targets", + }, } } diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_shared.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_shared.go index e28395740..e941efe66 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_shared.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_shared.go @@ -85,6 +85,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_target.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_target.go index b42bc7016..5756f912f 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_target.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/types_target.go @@ -41,12 +41,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -68,3 +68,14 @@ type TargetTemplate struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` } + +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` + + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` +} diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go index 7f939053c..b98779ad1 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go @@ -87,6 +87,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go index 49652852b..4f9d0261c 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go @@ -5,13 +5,9 @@ package v1alpha1 import ( - "encoding/json" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lsschema "github.com/gardener/landscaper/apis/schema" - - "github.com/gardener/landscaper/apis/core" ) // TargetType defines the type of the target. @@ -86,12 +82,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -114,69 +110,13 @@ type TargetTemplate struct { Annotations map[string]string `json:"annotations,omitempty"` } -////////////////////////////// -// Target Types // -////////////////////////////// -// todo: refactor to own package - -// KubernetesClusterTargetType defines the landscaper kubernetes cluster target. -const KubernetesClusterTargetType TargetType = core.GroupName + "/kubernetes-cluster" - -// KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config. -type KubernetesClusterTargetConfig struct { - // Kubeconfig defines kubeconfig as string. - Kubeconfig ValueRef `json:"kubeconfig"` -} - -// DefaultKubeconfigKey is the default that is used to hold a kubeconfig. -const DefaultKubeconfigKey = "kubeconfig" - -// ValueRef holds a value that can be either defined by string or by a secret ref. -type ValueRef struct { - StrVal *string `json:"-"` - - // deprecated - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// valueRefJSON is a helper struct to decode json into a secret ref object. -type valueRefJSON struct { - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// MarshalJSON implements the json marshaling for a JSON -func (v ValueRef) MarshalJSON() ([]byte, error) { - if v.StrVal != nil { - return json.Marshal(v.StrVal) - } - ref := valueRefJSON{ - SecretRef: v.SecretRef, - } - return json.Marshal(ref) -} - -// UnmarshalJSON implements json unmarshaling for a JSON -func (v *ValueRef) UnmarshalJSON(data []byte) error { - if data[0] == '"' { - var strVal string - if err := json.Unmarshal(data, &strVal); err != nil { - return err - } - v.StrVal = &strVal - return nil - } - ref := &valueRefJSON{} - if err := json.Unmarshal(data, ref); err != nil { - return err - } - v.SecretRef = ref.SecretRef - return nil -} +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` -func (v ValueRef) OpenAPISchemaType() []string { - return []string{ - "object", - "string", - } + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` } -func (v ValueRef) OpenAPISchemaFormat() string { return "" } diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go index 1bc724bad..843e08c09 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go @@ -652,6 +652,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ResolvedTarget)(nil), (*core.ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(a.(*ResolvedTarget), b.(*core.ResolvedTarget), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*core.ResolvedTarget)(nil), (*ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(a.(*core.ResolvedTarget), b.(*ResolvedTarget), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*ResourceReference)(nil), (*core.ResourceReference)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ResourceReference_To_core_ResourceReference(a.(*ResourceReference), b.(*core.ResourceReference), scope) }); err != nil { @@ -2633,6 +2643,28 @@ func Convert_core_Requirement_To_v1alpha1_Requirement(in *core.Requirement, out return autoConvert_core_Requirement_To_v1alpha1_Requirement(in, out, s) } +func autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + out.Target = (*core.Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget is an autogenerated conversion function. +func Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + return autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in, out, s) +} + +func autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + out.Target = (*Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget is an autogenerated conversion function. +func Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + return autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in, out, s) +} + func autoConvert_v1alpha1_ResourceReference_To_core_ResourceReference(in *ResourceReference, out *core.ResourceReference, s conversion.Scope) error { out.ComponentName = in.ComponentName out.ResourceName = in.ResourceName @@ -2945,10 +2977,8 @@ func Convert_core_TargetSelector_To_v1alpha1_TargetSelector(in *core.TargetSelec func autoConvert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.TargetSpec, s conversion.Scope) error { out.Type = core.TargetType(in.Type) - if err := Convert_v1alpha1_AnyJSON_To_core_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*core.SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*core.AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*core.LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -2959,10 +2989,8 @@ func Convert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.Ta func autoConvert_core_TargetSpec_To_v1alpha1_TargetSpec(in *core.TargetSpec, out *TargetSpec, s conversion.Scope) error { out.Type = TargetType(in.Type) - if err := Convert_core_AnyJSON_To_v1alpha1_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go index 18f570677..fdb8c0b5a 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -1724,23 +1724,6 @@ func (in *JSONSchemaDefinition) DeepCopy() *JSONSchemaDefinition { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesClusterTargetConfig) DeepCopyInto(out *KubernetesClusterTargetConfig) { - *out = *in - in.Kubeconfig.DeepCopyInto(&out.Kubeconfig) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesClusterTargetConfig. -func (in *KubernetesClusterTargetConfig) DeepCopy() *KubernetesClusterTargetConfig { - if in == nil { - return nil - } - out := new(KubernetesClusterTargetConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LocalSecretReference) DeepCopyInto(out *LocalSecretReference) { *out = *in @@ -1887,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2216,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return @@ -2428,32 +2436,6 @@ func (in *TypedObjectReference) DeepCopy() *TypedObjectReference { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ValueRef) DeepCopyInto(out *ValueRef) { - *out = *in - if in.StrVal != nil { - in, out := &in.StrVal, &out.StrVal - *out = new(string) - **out = **in - } - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValueRef. -func (in *ValueRef) DeepCopy() *ValueRef { - if in == nil { - return nil - } - out := new(ValueRef) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VersionedNamedObjectReference) DeepCopyInto(out *VersionedNamedObjectReference) { *out = *in diff --git a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go index d672d9267..0e3540f13 100644 --- a/controller-utils/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go +++ b/controller-utils/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go @@ -1870,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2199,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return diff --git a/docs/api-reference/core.md b/docs/api-reference/core.md index a393dff87..37698dc75 100644 --- a/docs/api-reference/core.md +++ b/docs/api-reference/core.md @@ -1342,6 +1342,10 @@ string

Target

+(Appears on: +ResolvedTarget) +

+

Target defines a specific data object that defines target environment. Every deploy item can have a target which is used by the deployer to install the specific application.

@@ -1430,8 +1434,8 @@ Exactly one of the fields Configuration and SecretRef must be set

secretRef
- -SecretReference + +LocalSecretReference @@ -4303,38 +4307,11 @@ encoding/json.RawMessage -

KubernetesClusterTargetConfig -

-

-

KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config.

-

- - - - - - - - - - - - - -
FieldDescription
-kubeconfig
- - -ValueRef - - -
-

Kubeconfig defines kubeconfig as string.

-

LocalSecretReference

(Appears on: +TargetSpec, TargetSyncSpec)

@@ -4572,6 +4549,47 @@ than on a single-element map, so we have a slice here.

+

ResolvedTarget +

+

+

ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference.

+

+ + + + + + + + + + + + + + + + + +
FieldDescription
+target
+ + +Target + + +
+

Target contains the original target.

+
+content
+ +string + +
+

Content contains the content of the target. +If the target has a secret reference, this field should be filled by a TargetResolver. +Otherwise, the inline configuration of the target is put here.

+

ResourceReference

@@ -4658,9 +4676,7 @@ string

(Appears on: -DataImport, -TargetSpec, -ValueRef) +DataImport)

SecretReference is reference to data in a secret. @@ -5189,8 +5205,8 @@ Exactly one of the fields Configuration and SecretRef must be set

secretRef
- -SecretReference + +LocalSecretReference @@ -5581,48 +5597,6 @@ ObjectReference -

ValueRef -

-

-(Appears on: -KubernetesClusterTargetConfig) -

-

-

ValueRef holds a value that can be either defined by string or by a secret ref.

-

- - - - - - - - - - - - - - - - - -
FieldDescription
--
- -string - -
-
-secretRef
- - -SecretReference - - -
-

deprecated

-

VersionedNamedObjectReference

diff --git a/docs/deployer/container.md b/docs/deployer/container.md index 2e1a0822f..6c6bacbaf 100644 --- a/docs/deployer/container.md +++ b/docs/deployer/container.md @@ -31,7 +31,9 @@ metadata: spec: type: landscaper.gardener.cloud/container - target: # has to be of type landscaper.gardener.cloud/kubernetes-cluster + target: + # only used to determine which instance of the container deployer is responsible, + # unless it is used explicitly in the custom code name: my-cluster namespace: test @@ -69,6 +71,62 @@ When the image with your program is executed, it gets access to particular infor corresponding DeployItem was deleted and some optional cleanup could be done. - *Imports* are provided as a json file at the path given by the env var `IMPORTS_PATH`. - *Exports* should be written to a json or yaml file at the path given by the env var `EXPORTS_PATH`. +- The content of the Target referenced in `.spec.target` is stored in a file at the path given by the env var `TARGET_PATH`. + - The file contains a json struct with two fields, `target` and `content`. + The first one contains the actual target, as it was read from the cluster, marshalled into json. + The second one contains the content of the target. If the target's `spec.secretRef` is set, this is the content of the resolved secret reference. Otherwise, the value is identical (JSON-wise) to the target's `spec.config`. + + **Example** + + Target: + ```yaml + apiVersion: landscaper.gardener.cloud/v1alpha1 + kind: Target + metadata: + name: my-target + namespace: my-namespace + spec: + type: landscaper.gardener.cloud/my-target-type + secretRef: + key: secret1 + name: my-secret + ``` + + Secret: + ```yaml + apiVersion: v1 + kind: Secret + metadata: + name: my-secret + namespace: my-namespace + type: Opaque + data: + secret1: "eyJmb28iOiAiYmFyIn0=" # {"foo": "bar"} + ``` + + Content of the file at `TARGET_PATH`: + ```json + { + "target": { + "kind": "Target", + "apiVersion": "landscaper.gardener.cloud/v1alpha1", + "metadata": { + "name": "my-target", + "namespace": "my-namespace", + }, + "spec": { + "type": "landscaper.gardener.cloud/my-target-type", + "secretRef": { + "name": "my-secret", + "key": "secret1" + } + } + }, + "content": "{\"foo\": \"bar\"}" + } + ``` + + If the target didn't contain a secret reference, but `config: {foo: bar}` instead, the value of the `content` field would be the same. - An optional *state* should be written to the directory given by the env var `STATE_PATH`. The complete state directory will be tarred and managed by Landscaper(:warning: no symlinks). The last state data are provided in the next execution or your program. diff --git a/docs/technical/target_types.md b/docs/technical/target_types.md index ff437065f..4410f0612 100644 --- a/docs/technical/target_types.md +++ b/docs/technical/target_types.md @@ -12,7 +12,7 @@ The target type `landscaper.gardener.cloud/kubernetes-cluster` contains the acce **Type**: `landscaper.gardener.cloud/kubernetes-cluster` -There are three variants for the configuration of targets of type `landscaper.gardener.cloud/kubernetes-cluster` +There are two variants for the configuration of targets of type `landscaper.gardener.cloud/kubernetes-cluster` **Config Variant 1**: @@ -34,24 +34,7 @@ spec: **Config Variant 2**: -This variant contains the kubeconfig in a secrets referenced in the `config` section. The key in the data section, where -to find the kubeconfig, could be specified. Currently, the secret must be in the same namespace as the target. - -```yaml -apiVersion: landscaper.gardener.cloud/v1alpha1 -kind: Target -metadata: - name: ... - namespace: ... -spec: - config: - secretRef: - name: my-secret - namespace: default - key: kubeconfig # optional will default to "kubeconfig" -``` - -**Config Variant 3**: +**DEPRECATED: use the Target's `spec.secretRef` field instead** This variant contains the kubeconfig in a secrets referenced under an entry `kubeconfig` in the `config` section. The key in the data section, where to find the kubeconfig, could be specified. This is the old format before secret references diff --git a/docs/usage/Targets.md b/docs/usage/Targets.md index 07e78ede1..b7269d205 100644 --- a/docs/usage/Targets.md +++ b/docs/usage/Targets.md @@ -7,3 +7,148 @@ a fenced environment and needs to be handled by another deployer instance). The configuration structure of targets is defined by their type (currently the type is only for identification but later we plan to add some type registration with checks.) + +## Inline Configuration vs. Secret Reference + +The content of a Target can be provided in two different ways: either inline in the Target, or as a reference to a secret containing the actual value. + +All of the example Targets given below result in the same Target content. + +### Inline Configuration + +The easiest way is to just put the configuration inline into the Target's `config` field: + +```yaml +apiVersion: landscaper.gardener.cloud/v1alpha1 +kind: Target +metadata: + name: my-cluster +spec: + type: landscaper.gardener.cloud/kubernetes-cluster + config: + kubeconfig: | + apiVersion: v1 + clusters: + - cluster: + certificate-authority-data: ... + server: https://my-apiserver.example.com + name: default + contexts: + - context: + cluster: default + user: default + name: default + current-context: default + kind: Config + preferences: {} + users: + - name: default + user: + token: ... +``` + +### Secret Reference + +Alternatively, the Target can also reference a secret containing the actual value instead: + +Secret: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: cluster-access +type: Opaque +data: + kubeconfig: | + YXBpVmVyc2lvbjogdjEKY2x1c3RlcnM6Ci0gY2x1c3RlcjoKICAgIGNlcnRpZmljYXRlLWF1dGhvcml0eS1kYXRhOiAuLi4KICAgIHNlcnZlcjogaHR0cHM6Ly9teS1hcGlzZXJ2ZXIuZXhhbXBsZS5jb20KICBuYW1lOiBkZWZhdWx0CmNvbnRleHRzOgotIGNvbnRleHQ6CiAgICBjbHVzdGVyOiBkZWZhdWx0CiAgICB1c2VyOiBkZWZhdWx0CiAgbmFtZTogZGVmYXVsdApjdXJyZW50LWNvbnRleHQ6IGRlZmF1bHQKa2luZDogQ29uZmlnCnByZWZlcmVuY2VzOiB7fQp1c2VyczoKLSBuYW1lOiBkZWZhdWx0CiAgdXNlcjoKICAgIHRva2VuOiAuLi4K + # decoded value for convenience: + # apiVersion: v1 + # clusters: + # - cluster: + # certificate-authority-data: ... + # server: https://my-apiserver.example.com + # name: default + # contexts: + # - context: + # cluster: default + # user: default + # name: default + # current-context: default + # kind: Config + # preferences: {} + # users: + # - name: default + # user: + # token: ... + +``` + +Target: +```yaml +apiVersion: landscaper.gardener.cloud/v1alpha1 +kind: Target +metadata: + name: my-cluster +spec: + type: landscaper.gardener.cloud/kubernetes-cluster + secretRef: + name: cluster-access +``` + +Targets can only reference secrets in their own namespace. + +It is also possible to not reference a complete secret - in which case the complete structure from its `data` field will be interpreted as the Target's content - but only the value of a specific key. To do so, add the `key` to the secret reference: + +Secret: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: cluster-access +type: Opaque +data: + cluster1: | + a3ViZWNvbmZpZzogfAogIGFwaVZlcnNpb246IHYxCiAgY2x1c3RlcnM6CiAgLSBjbHVzdGVyOgogICAgICBjZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YTogLi4uCiAgICAgIHNlcnZlcjogaHR0cHM6Ly9teS1hcGlzZXJ2ZXIuZXhhbXBsZS5jb20KICAgIG5hbWU6IGRlZmF1bHQKICBjb250ZXh0czoKICAtIGNvbnRleHQ6CiAgICAgIGNsdXN0ZXI6IGRlZmF1bHQKICAgICAgdXNlcjogZGVmYXVsdAogICAgbmFtZTogZGVmYXVsdAogIGN1cnJlbnQtY29udGV4dDogZGVmYXVsdAogIGtpbmQ6IENvbmZpZwogIHByZWZlcmVuY2VzOiB7fQogIHVzZXJzOgogIC0gbmFtZTogZGVmYXVsdAogICAgdXNlcjoKICAgICAgdG9rZW46IC4uLgo= + # decoded value for convenience: + # kubeconfig: | + # apiVersion: v1 + # clusters: + # - cluster: + # certificate-authority-data: ... + # server: https://my-apiserver.example.com + # name: default + # contexts: + # - context: + # cluster: default + # user: default + # name: default + # current-context: default + # kind: Config + # preferences: {} + # users: + # - name: default + # user: + # token: ... +``` + +Target: +```yaml +apiVersion: landscaper.gardener.cloud/v1alpha1 +kind: Target +metadata: + name: my-cluster +spec: + type: landscaper.gardener.cloud/kubernetes-cluster + secretRef: + name: cluster-access + key: cluster1 +``` + +Note that the value of `cluster1` in the secret now not only contains the kubeconfig, but a struct with a `kubeconfig` key instead. + + +#### Resolving Secret References + +The deployers have to take care of resolving secret references in Targets. If the deployer library is used, this is handled by the library and the functions which have to be implemented by the deployer get the already resolved Target in form of a [ResolvedTarget](../api-reference/core.md#resolvedtarget) struct. This struct has a `Content` field which contains the content of the Target, independently of whether it was specified inline or via a reference in the Target. + +If you write your own deployer without using the deployer library, you will have to take care of resolving secret references in Targets yourself. diff --git a/hack/install-landscaper-for-integration-test.sh b/hack/install-landscaper-for-integration-test.sh index 3628bf3f2..3f65c943b 100755 --- a/hack/install-landscaper-for-integration-test.sh +++ b/hack/install-landscaper-for-integration-test.sh @@ -62,8 +62,8 @@ landscaper: - manifest - mock deployItemTimeouts: - pickup: 10s - abort: 10s + pickup: 30s + abort: 30s " > /tmp/values.yaml touch /tmp/registry-values.yaml diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index c493cc8c7..84f0efaad 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -30,6 +30,7 @@ import ( "github.com/gardener/landscaper/apis/config" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" lc "github.com/gardener/landscaper/controller-utils/pkg/logging/constants" ) @@ -67,13 +68,10 @@ func New(lsClient client.Client, // like the Environment and the Target are registered in the landscaper cluster. func (a *Agent) EnsureLandscaperResources(ctx context.Context, lsClient, hostClient client.Client) (*lsv1alpha1.Environment, error) { logger, ctx := logging.FromContextOrNew(ctx, nil, lc.KeyMethod, "EnsureLandscaperResources") - target, err := utils.NewTargetBuilder(string(lsv1alpha1.KubernetesClusterTargetType)). - SecretRef(&lsv1alpha1.SecretReference{ - ObjectReference: lsv1alpha1.ObjectReference{ - Name: a.TargetSecretName(), - Namespace: a.config.LandscaperNamespace, - }, - Key: lsv1alpha1.DefaultKubeconfigKey, + target, err := utils.NewTargetBuilder(string(targettypes.KubernetesClusterTargetType)). + SecretRef(&lsv1alpha1.LocalSecretReference{ + Name: a.TargetSecretName(), + Key: targettypes.DefaultKubeconfigKey, }).Build() if err != nil { return nil, err @@ -194,7 +192,7 @@ func (a *Agent) EnsureHostResources(ctx context.Context, hostClient, lsClient cl if _, err := controllerutil.CreateOrUpdate(ctx, lsClient, secret, func() error { secret.Data = map[string][]byte{ - lsv1alpha1.DefaultKubeconfigKey: kubeconfigBytes, + targettypes.DefaultKubeconfigKey: kubeconfigBytes, } return nil }); err != nil { diff --git a/pkg/deployer/container/container.go b/pkg/deployer/container/container.go index 748d97ca4..2685c8702 100644 --- a/pkg/deployer/container/container.go +++ b/pkg/deployer/container/container.go @@ -55,6 +55,7 @@ type Container struct { Context *lsv1alpha1.Context ProviderStatus *containerv1alpha1.ProviderStatus ProviderConfiguration *containerv1alpha1.ProviderConfiguration + Target *lsv1alpha1.ResolvedTarget InitContainerServiceAccountSecret types.NamespacedName WaitContainerServiceAccountSecret types.NamespacedName @@ -69,7 +70,8 @@ func New(lsClient, config containerv1alpha1.Configuration, item *lsv1alpha1.DeployItem, lsCtx *lsv1alpha1.Context, - sharedCache cache.Cache) (*Container, error) { + sharedCache cache.Cache, + rt *lsv1alpha1.ResolvedTarget) (*Container, error) { providerConfig := &containerv1alpha1.ProviderConfiguration{} decoder := api.NewDecoder(Scheme) if _, _, err := decoder.Decode(item.Spec.Configuration.Raw, nil, providerConfig); err != nil { @@ -100,6 +102,7 @@ func New(lsClient, ProviderStatus: status, ProviderConfiguration: providerConfig, sharedCache: sharedCache, + Target: rt, }, nil } diff --git a/pkg/deployer/container/container_reconcile.go b/pkg/deployer/container/container_reconcile.go index 9f468954e..4d165ff5a 100644 --- a/pkg/deployer/container/container_reconcile.go +++ b/pkg/deployer/container/container_reconcile.go @@ -98,6 +98,11 @@ func (c *Container) Reconcile(ctx context.Context, operation container.Operation operationName, "SyncConfiguration", err.Error()) } + if err := c.SyncTarget(ctx, defaultLabels); err != nil { + return lserrors.NewWrappedError(err, + operationName, "SyncTarget", err.Error()) + } + imagePullSecret, blueprintSecret, componentDescriptorSecret, err := c.parseAndSyncSecrets(ctx, defaultLabels) if err != nil { return lserrors.NewWrappedError(err, @@ -121,6 +126,7 @@ func (c *Container) Reconcile(ctx context.Context, operation container.Operation InitContainerServiceAccountSecret: c.InitContainerServiceAccountSecret, WaitContainerServiceAccountSecret: c.WaitContainerServiceAccountSecret, ConfigurationSecretName: ConfigurationSecretName(c.DeployItem.Namespace, c.DeployItem.Name), + TargetSecretName: TargetSecretName(c.DeployItem.Namespace, c.DeployItem.Name), ImagePullSecret: imagePullSecret, BluePrintPullSecret: blueprintSecret, @@ -618,6 +624,28 @@ func (c *Container) SyncConfiguration(ctx context.Context, defaultLabels map[str return nil } +// SyncTarget syncs the deployitem's target content as secret to the host cluster. +func (c *Container) SyncTarget(ctx context.Context, defaultLabels map[string]string) error { + secret := &corev1.Secret{} + secret.Name = TargetSecretName(c.DeployItem.Namespace, c.DeployItem.Name) + secret.Namespace = c.Configuration.Namespace + if _, err := controllerutil.CreateOrUpdate(ctx, c.directHostClient, secret, func() error { + InjectDefaultLabels(secret, defaultLabels) + kutil.SetMetaDataLabel(&secret.ObjectMeta, container.ContainerDeployerTypeLabel, "target") + data, err := json.Marshal(c.Target) + if err != nil { + return fmt.Errorf("error marshalling resolvedtarget struct into json: %w", err) + } + secret.Data = map[string][]byte{ + container.TargetFileName: data, + } + return nil + }); err != nil { + return fmt.Errorf("unable to sync target content to host cluster: %w", err) + } + return nil +} + // SyncExport syncs the export secret from the wait container to the deploy item export. func (c *Container) SyncExport(ctx context.Context) error { log, ctx := logging.FromContextOrNew(ctx, nil) diff --git a/pkg/deployer/container/init/e2e_test.go b/pkg/deployer/container/init/e2e_test.go index 8f85d8546..b47cfe41f 100644 --- a/pkg/deployer/container/init/e2e_test.go +++ b/pkg/deployer/container/init/e2e_test.go @@ -57,6 +57,7 @@ var _ = Describe("Init e2e", func() { Expect(os.Setenv(container.StatePathName, container.StatePath)).To(Succeed()) Expect(os.Setenv(container.ContentPathName, container.ContentPath)).To(Succeed()) Expect(os.Setenv(container.ComponentDescriptorPathName, container.ComponentDescriptorPath)).To(Succeed()) + Expect(os.Setenv(container.TargetPathName, container.TargetPath)).To(Succeed()) }) AfterEach(func() { @@ -80,6 +81,9 @@ var _ = Describe("Init e2e", func() { utils.ExpectNoError(os.Setenv(container.DeployItemNamespaceName, di.Namespace)) utils.ExpectNoError(os.Setenv(container.PodNamespaceName, testState.Namespace)) + Expect(resFs.MkdirAll(container.TargetInitDir, os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(resFs, filepath.Join(container.TargetInitDir, container.TargetFileName), []byte(`{"foo": "bar"}`), os.ModePerm)).To(Succeed()) // fake target file + utils.ExpectNoError(fs.MkdirAll(container.StatePath, os.ModePerm)) utils.ExpectNoError(vfs.WriteFile(fs, testFilePath, testData, os.ModePerm)) diff --git a/pkg/deployer/container/init/init.go b/pkg/deployer/container/init/init.go index a20c39b24..ccbc37103 100644 --- a/pkg/deployer/container/init/init.go +++ b/pkg/deployer/container/init/init.go @@ -10,6 +10,7 @@ import ( "fmt" "os" "path" + "path/filepath" "github.com/gardener/component-cli/ociclient" "github.com/gardener/component-spec/bindings-go/ctf" @@ -32,6 +33,7 @@ import ( "github.com/gardener/component-cli/ociclient/credentials" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + containercore "github.com/gardener/landscaper/apis/deployer/container" containerv1alpha1 "github.com/gardener/landscaper/apis/deployer/container/v1alpha1" "github.com/gardener/landscaper/pkg/api" "github.com/gardener/landscaper/pkg/deployer/container" @@ -97,6 +99,9 @@ func run(ctx context.Context, opts *options, kubeClient client.Client, fs vfs.Fi if err := fs.MkdirAll(path.Dir(opts.ComponentDescriptorFilePath), os.ModePerm); err != nil { return err } + if err := fs.MkdirAll(path.Dir(opts.TargetFilePath), os.ModePerm); err != nil { + return err + } if err := fs.MkdirAll(opts.ContentDirPath, os.ModePerm); err != nil { return err } @@ -160,6 +165,17 @@ func run(ctx context.Context, opts *options, kubeClient client.Client, fs vfs.Fi } } + // copy target to shared volume + targetSource := filepath.Join(containercore.TargetInitDir, containercore.TargetFileName) + targetContent, err := vfs.ReadFile(fs, targetSource) + if err != nil { + return fmt.Errorf("error reading target content from '%s': %w", targetSource, err) + } + if err := vfs.WriteFile(fs, opts.TargetFilePath, targetContent, os.ModePerm); err != nil { + return fmt.Errorf("error writing target content to '%s': %w", opts.TargetFilePath, err) + } + log.Info("Copied target content to shared volume.") + log.Info("Restoring state") if err := state.New(kubeClient, opts.podNamespace, opts.DeployItemKey, opts.StateDirPath).WithFs(fs).Restore(ctx); err != nil { return err diff --git a/pkg/deployer/container/init/init_suite_test.go b/pkg/deployer/container/init/init_suite_test.go index 6586a4fbf..613162b1f 100644 --- a/pkg/deployer/container/init/init_suite_test.go +++ b/pkg/deployer/container/init/init_suite_test.go @@ -37,6 +37,7 @@ var _ = Describe("Constructor", func() { var ( ctrl *gomock.Controller fakeClient *mock_client.MockClient + fs vfs.FileSystem ) BeforeEach(func() { @@ -49,10 +50,15 @@ var _ = Describe("Constructor", func() { Expect(os.Setenv(container.StatePathName, container.StatePath)).To(Succeed()) Expect(os.Setenv(container.ContentPathName, container.ContentPath)).To(Succeed()) Expect(os.Setenv(container.ComponentDescriptorPathName, container.ComponentDescriptorPath)).To(Succeed()) + Expect(os.Setenv(container.TargetPathName, container.TargetPath)).To(Succeed()) utils.ExpectNoError(os.Setenv(container.DeployItemName, "dummy")) utils.ExpectNoError(os.Setenv(container.DeployItemNamespaceName, "val")) utils.ExpectNoError(os.Setenv(container.PodNamespaceName, "default")) + + fs = memoryfs.New() + Expect(fs.MkdirAll(container.TargetInitDir, os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, filepath.Join(container.TargetInitDir, container.TargetFileName), []byte(`{"foo": "bar"}`), os.ModePerm)).To(Succeed()) // fake target file }) AfterEach(func() { @@ -65,15 +71,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/00-di-simple.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - dataBytes, err := vfs.ReadFile(memFs, container.ImportsPath) + dataBytes, err := vfs.ReadFile(fs, container.ImportsPath) Expect(err).ToNot(HaveOccurred()) var data interface{} Expect(json.Unmarshal(dataBytes, &data)).To(Succeed()) @@ -86,15 +91,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/01-di-blueprint.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - info, err := vfs.ReadDir(memFs, container.ContentPath) + info, err := vfs.ReadDir(fs, container.ContentPath) Expect(err).ToNot(HaveOccurred()) Expect(info).To(HaveLen(1)) Expect(info[0].Name()).To(Equal(v1alpha1.BlueprintFileName)) @@ -106,15 +110,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/02-di-inline-blueprint.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - info, err := vfs.ReadDir(memFs, container.ContentPath) + info, err := vfs.ReadDir(fs, container.ContentPath) Expect(err).ToNot(HaveOccurred()) Expect(info).To(HaveLen(1)) Expect(info[0].Name()).To(Equal(v1alpha1.BlueprintFileName)) @@ -126,15 +129,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/01-di-blueprint.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - data, err := vfs.ReadFile(memFs, container.ComponentDescriptorPath) + data, err := vfs.ReadFile(fs, container.ComponentDescriptorPath) Expect(err).ToNot(HaveOccurred()) cd := &cdv2.ComponentDescriptorList{} @@ -148,15 +150,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/03-di-inline-cd.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - data, err := vfs.ReadFile(memFs, container.ComponentDescriptorPath) + data, err := vfs.ReadFile(fs, container.ComponentDescriptorPath) Expect(err).ToNot(HaveOccurred()) cd := &cdv2.ComponentDescriptorList{} @@ -170,15 +171,14 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/04-di-inline-bp-no-cd.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - info, err := vfs.ReadDir(memFs, container.ContentPath) + info, err := vfs.ReadDir(fs, container.ContentPath) Expect(err).ToNot(HaveOccurred()) Expect(info).To(HaveLen(1)) Expect(info[0].Name()).To(Equal(v1alpha1.BlueprintFileName)) @@ -190,20 +190,19 @@ var _ = Describe("Constructor", func() { fakeClient.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "")) opts := &options{} opts.Complete() - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/05-di-inline-bp-inline-cd.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - info, err := vfs.ReadDir(memFs, container.ContentPath) + info, err := vfs.ReadDir(fs, container.ContentPath) Expect(err).ToNot(HaveOccurred()) Expect(info).To(HaveLen(1)) Expect(info[0].Name()).To(Equal(v1alpha1.BlueprintFileName)) - data, err := vfs.ReadFile(memFs, container.ComponentDescriptorPath) + data, err := vfs.ReadFile(fs, container.ComponentDescriptorPath) Expect(err).ToNot(HaveOccurred()) cd := &cdv2.ComponentDescriptorList{} Expect(codec.Decode(data, cd)).To(Succeed()) @@ -217,15 +216,14 @@ var _ = Describe("Constructor", func() { opts := &options{} opts.Complete() opts.RegistrySecretBasePath = "/unexisting/path" - memFs := memoryfs.New() file, err := os.ReadFile("./testdata/01-di-blueprint.yaml") Expect(err).ToNot(HaveOccurred()) - Expect(memFs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) - Expect(vfs.WriteFile(memFs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) - Expect(run(ctx, opts, fakeClient, memFs)).To(Succeed()) + Expect(fs.MkdirAll(filepath.Dir(container.ConfigurationPath), os.ModePerm)).To(Succeed()) + Expect(vfs.WriteFile(fs, container.ConfigurationPath, file, os.ModePerm)).To(Succeed()) + Expect(run(ctx, opts, fakeClient, fs)).To(Succeed()) - info, err := vfs.ReadDir(memFs, container.ContentPath) + info, err := vfs.ReadDir(fs, container.ContentPath) Expect(err).ToNot(HaveOccurred()) Expect(info).To(HaveLen(1)) Expect(info[0].Name()).To(Equal(v1alpha1.BlueprintFileName)) diff --git a/pkg/deployer/container/init/options.go b/pkg/deployer/container/init/options.go index 546e152d1..3dfb859c6 100644 --- a/pkg/deployer/container/init/options.go +++ b/pkg/deployer/container/init/options.go @@ -26,6 +26,7 @@ type options struct { ImportsFilePath string ExportsFilePath string ComponentDescriptorFilePath string + TargetFilePath string ContentDirPath string StateDirPath string RegistrySecretBasePath string @@ -44,6 +45,7 @@ func (o *options) Complete() { o.ImportsFilePath = os.Getenv(container.ImportsPathName) o.ExportsFilePath = os.Getenv(container.ExportsPathName) o.ComponentDescriptorFilePath = os.Getenv(container.ComponentDescriptorPathName) + o.TargetFilePath = os.Getenv(container.TargetPathName) o.ContentDirPath = os.Getenv(container.ContentPathName) o.StateDirPath = os.Getenv(container.StatePathName) o.RegistrySecretBasePath = os.Getenv(container.RegistrySecretBasePathName) @@ -76,6 +78,9 @@ func (o *options) Validate() error { if len(o.ComponentDescriptorFilePath) == 0 { err = multierror.Append(err, fmt.Errorf("%s has to be defined", container.ComponentDescriptorPathName)) } + if len(o.TargetFilePath) == 0 { + err = multierror.Append(err, fmt.Errorf("%s has to be defined", container.TargetPathName)) + } if len(o.ContentDirPath) == 0 { err = multierror.Append(err, fmt.Errorf("%s has to be defined", container.ContentPathName)) } diff --git a/pkg/deployer/container/pod.go b/pkg/deployer/container/pod.go index 387cf2f7c..1b64adce8 100644 --- a/pkg/deployer/container/pod.go +++ b/pkg/deployer/container/pod.go @@ -57,6 +57,12 @@ func ConfigurationSecretName(deployItemNamespace, deployItemName string) string return fmt.Sprintf("%s-%s-config", deployItemNamespace, deployItemName) } +// TargetSecretName generates the secret name for the imported secret. +// todo: use container identity +func TargetSecretName(deployItemNamespace, deployItemName string) string { + return fmt.Sprintf("%s-%s-target", deployItemNamespace, deployItemName) +} + // ImagePullSecretName generates the secret name for the image pull secret. // todo: use container identity func ImagePullSecretName(deployItemNamespace, deployItemName string) string { @@ -107,6 +113,7 @@ type PodOptions struct { InitContainerServiceAccountSecret types.NamespacedName WaitContainerServiceAccountSecret types.NamespacedName ConfigurationSecretName string + TargetSecretName string ImagePullSecret string BluePrintPullSecret string ComponentDescriptorPullSecret string @@ -193,6 +200,20 @@ func generatePod(opts PodOptions) (*corev1.Pod, error) { MountPath: filepath.Dir(container.ConfigurationPath), } + targetVolume := corev1.Volume{ + Name: "target", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: opts.TargetSecretName, + }, + }, + } + targetInitVolumeMount := corev1.VolumeMount{ + Name: targetVolume.Name, + ReadOnly: true, + MountPath: container.TargetInitDir, + } + additionalInitEnvVars := []corev1.EnvVar{ { Name: container.ConfigurationPathName, @@ -233,9 +254,10 @@ func generatePod(opts PodOptions) (*corev1.Pod, error) { waitServiceAccountVolume, sharedVolume, configurationVolume, + targetVolume, } - initMounts := []corev1.VolumeMount{configurationVolumeMount, initServiceAccountMount, sharedVolumeMount} + initMounts := []corev1.VolumeMount{configurationVolumeMount, targetInitVolumeMount, initServiceAccountMount, sharedVolumeMount} for name, v := range map[string]string{ "blueprint-pull-secret": opts.BluePrintPullSecret, diff --git a/pkg/deployer/container/reconciler_deployitem.go b/pkg/deployer/container/reconciler_deployitem.go index 369c81562..a4cc33d3c 100644 --- a/pkg/deployer/container/reconciler_deployitem.go +++ b/pkg/deployer/container/reconciler_deployitem.go @@ -68,23 +68,23 @@ type deployer struct { hooks extension.ReconcileExtensionHooks } -func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.Target) error { - containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, lsCtx, d.sharedCache) +func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, lsCtx, d.sharedCache, rt) if err != nil { return err } return containerOp.Reconcile(ctx, container.OperationReconcile) } -func (d deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { - containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, lsCtx, d.sharedCache) +func (d deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, lsCtx, d.sharedCache, rt) if err != nil { return err } return containerOp.Delete(ctx) } -func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { +func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.ResolvedTarget) error { d.log.Info("abort is not yet implemented") return nil } @@ -95,7 +95,7 @@ func (d *deployer) ExtensionHooks() extension.ReconcileExtensionHooks { func (d *deployer) NextReconcile(ctx context.Context, last time.Time, di *lsv1alpha1.DeployItem) (*time.Time, error) { // TODO: parse provider configuration directly and do not init the container helper struct - containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, nil, d.sharedCache) + containerOp, err := New(d.lsClient, d.hostClient, d.directHostClient, d.config, di, nil, d.sharedCache, nil) if err != nil { return nil, err } diff --git a/pkg/deployer/helm/deployer.go b/pkg/deployer/helm/deployer.go index 14d416243..c32e6b401 100644 --- a/pkg/deployer/helm/deployer.go +++ b/pkg/deployer/helm/deployer.go @@ -62,8 +62,8 @@ type deployer struct { hooks extension.ReconcileExtensionHooks } -func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { - helm, err := New(d.config, d.lsClient, d.hostClient, di, target, lsCtx, d.sharedCache) +func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + helm, err := New(d.config, d.lsClient, d.hostClient, di, rt, lsCtx, d.sharedCache) if err != nil { err = lserrors.NewWrappedError(err, "Reconcile", "New", err.Error()) return err @@ -84,8 +84,8 @@ func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di return helm.ApplyFiles(ctx, files, crds, exports, ch) } -func (d *deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { - helm, err := New(d.config, d.lsClient, d.hostClient, di, target, lsCtx, d.sharedCache) +func (d *deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + helm, err := New(d.config, d.lsClient, d.hostClient, di, rt, lsCtx, d.sharedCache) if err != nil { return err } @@ -93,7 +93,7 @@ func (d *deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *ls return helm.DeleteFiles(ctx) } -func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { +func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { d.log.Info("abort is not yet implemented") return nil } diff --git a/pkg/deployer/helm/helm.go b/pkg/deployer/helm/helm.go index d8e1784eb..b8846df06 100644 --- a/pkg/deployer/helm/helm.go +++ b/pkg/deployer/helm/helm.go @@ -7,7 +7,6 @@ package helm import ( "context" "encoding/base64" - "encoding/json" "errors" "fmt" @@ -19,13 +18,14 @@ import ( "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/engine" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" helminstall "github.com/gardener/landscaper/apis/deployer/helm/install" helmv1alpha1 "github.com/gardener/landscaper/apis/deployer/helm/v1alpha1" helmv1alpha1validation "github.com/gardener/landscaper/apis/deployer/helm/v1alpha1/validation" @@ -63,7 +63,7 @@ type Helm struct { Configuration helmv1alpha1.Configuration DeployItem *lsv1alpha1.DeployItem - Target *lsv1alpha1.Target + Target *lsv1alpha1.ResolvedTarget Context *lsv1alpha1.Context ProviderConfiguration *helmv1alpha1.ProviderConfiguration ProviderStatus *helmv1alpha1.ProviderStatus @@ -79,7 +79,7 @@ func New(helmconfig helmv1alpha1.Configuration, lsKubeClient client.Client, hostKubeClient client.Client, item *lsv1alpha1.DeployItem, - target *lsv1alpha1.Target, + rt *lsv1alpha1.ResolvedTarget, lsCtx *lsv1alpha1.Context, sharedCache cache.Cache) (*Helm, error) { @@ -110,7 +110,7 @@ func New(helmconfig helmv1alpha1.Configuration, hostKubeClient: hostKubeClient, Configuration: helmconfig, DeployItem: item, - Target: target, + Target: rt, Context: lsCtx, ProviderConfiguration: config, ProviderStatus: status, @@ -217,24 +217,14 @@ func (h *Helm) TargetClient(ctx context.Context) (*rest.Config, client.Client, k return restConfig, kubeClient, clientset, nil } if h.Target != nil { - var kubeconfigBytes []byte - var err error - - if h.Target.Spec.SecretRef != nil { - kubeconfigBytes, err = lib.GetKubeconfigFromSecretRef(ctx, h.Target.Spec.SecretRef, h.Target.Namespace, h.lsKubeClient) - if err != nil { - return nil, nil, nil, err - } - } else { - targetConfig := &lsv1alpha1.KubernetesClusterTargetConfig{} - if err = json.Unmarshal(h.Target.Spec.Configuration.RawMessage, targetConfig); err != nil { - return nil, nil, nil, fmt.Errorf("unable to parse target confíguration: %w", err) - } - - kubeconfigBytes, err = lib.GetKubeconfigFromTargetConfig(ctx, targetConfig, h.Target.Namespace, h.lsKubeClient) - if err != nil { - return nil, nil, nil, err - } + targetConfig := &targettypes.KubernetesClusterTargetConfig{} + if err := yaml.Unmarshal([]byte(h.Target.Content), targetConfig); err != nil { + return nil, nil, nil, fmt.Errorf("unable to parse target confíguration: %w", err) + } + + kubeconfigBytes, err := lib.GetKubeconfigFromTargetConfig(ctx, targetConfig, h.Target.Namespace, h.lsKubeClient) + if err != nil { + return nil, nil, nil, err } kubeconfig, err := clientcmd.NewClientConfigFromBytes(kubeconfigBytes) diff --git a/pkg/deployer/helm/test/e2e_test.go b/pkg/deployer/helm/test/e2e_test.go index 08b6ebfad..e1b2614c7 100644 --- a/pkg/deployer/helm/test/e2e_test.go +++ b/pkg/deployer/helm/test/e2e_test.go @@ -21,6 +21,7 @@ import ( deployerlib "github.com/gardener/landscaper/pkg/deployer/lib" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" "github.com/gardener/landscaper/apis/deployer/helm" helmv1alpha1 "github.com/gardener/landscaper/apis/deployer/helm/v1alpha1" "github.com/gardener/landscaper/apis/deployer/helm/v1alpha1/helper" @@ -91,9 +92,9 @@ var _ = Describe("Helm Deployer", func() { testenv.Client, di.Spec.Target.Namespace, di.Spec.Target.Name, - string(lsv1alpha1.KubernetesClusterTargetType), - lsv1alpha1.KubernetesClusterTargetConfig{ - Kubeconfig: lsv1alpha1.ValueRef{ + string(targettypes.KubernetesClusterTargetType), + targettypes.KubernetesClusterTargetConfig{ + Kubeconfig: targettypes.ValueRef{ StrVal: pointer.StringPtr(string(kubeconfigBytes)), }, }, diff --git a/pkg/deployer/lib/controller.go b/pkg/deployer/lib/controller.go index 994231092..ce67bebbc 100644 --- a/pkg/deployer/lib/controller.go +++ b/pkg/deployer/lib/controller.go @@ -35,6 +35,8 @@ import ( "github.com/gardener/landscaper/controller-utils/pkg/logging" lc "github.com/gardener/landscaper/controller-utils/pkg/logging/constants" "github.com/gardener/landscaper/pkg/deployer/lib/extension" + "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver" + secretresolver "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver/secret" "github.com/gardener/landscaper/pkg/deployer/lib/targetselector" "github.com/gardener/landscaper/pkg/utils/read_write_layer" "github.com/gardener/landscaper/pkg/version" @@ -43,11 +45,11 @@ import ( // Deployer defines a controller that acts upon deployitems. type Deployer interface { // Reconcile the deployitem. - Reconcile(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error + Reconcile(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.ResolvedTarget) error // Delete the deployitem. - Delete(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error + Delete(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.ResolvedTarget) error // Abort the deployitem progress. - Abort(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error + Abort(ctx context.Context, lsContext *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.ResolvedTarget) error // ExtensionHooks returns all registered extension hooks. ExtensionHooks() extension.ReconcileExtensionHooks } @@ -214,11 +216,25 @@ func (c *controller) Reconcile(ctx context.Context, req reconcile.Request) (reco } } + // resolve Target reference, if any + var rt *lsv1alpha1.ResolvedTarget + if target != nil { + if target.Spec.SecretRef != nil { + sr := secretresolver.New(c.lsClient) + rt, err = sr.Resolve(ctx, target) + if err != nil { + return reconcile.Result{}, fmt.Errorf("error resolving secret reference (%s/%s#%s) in target '%s/%s': %w", target.Namespace, target.Spec.SecretRef.Name, target.Spec.SecretRef.Key, target.Namespace, target.Name, err) + } + } else { + rt = targetresolver.NewResolvedTarget(target) + } + } + var err lserrors.LsError if di.DeletionTimestamp.IsZero() { - err = c.reconcile(ctx, lsCtx, di, target) + err = c.reconcile(ctx, lsCtx, di, rt) } else { - err = c.delete(ctx, lsCtx, di, target) + err = c.delete(ctx, lsCtx, di, rt) } return reconcile.Result{}, c.handleReconcileResult(ctx, err, old, di) } else { @@ -258,7 +274,7 @@ func (c *controller) checkTargetResponsibility(ctx context.Context, log logging. return target, true, nil } -func (c *controller) reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, deployItem *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) lserrors.LsError { +func (c *controller) reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, deployItem *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) lserrors.LsError { if !controllerutil.ContainsFinalizer(deployItem, lsv1alpha1.LandscaperFinalizer) { controllerutil.AddFinalizer(deployItem, lsv1alpha1.LandscaperFinalizer) if err := c.Writer().UpdateDeployItem(ctx, read_write_layer.W000050, deployItem); err != nil { @@ -267,17 +283,17 @@ func (c *controller) reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, d } } - err := c.deployer.Reconcile(ctx, lsCtx, deployItem, target) + err := c.deployer.Reconcile(ctx, lsCtx, deployItem, rt) return lserrors.BuildLsErrorOrNil(err, "reconcile", "Reconcile") } func (c *controller) delete(ctx context.Context, lsCtx *lsv1alpha1.Context, deployItem *lsv1alpha1.DeployItem, - target *lsv1alpha1.Target) lserrors.LsError { + rt *lsv1alpha1.ResolvedTarget) lserrors.LsError { logger, ctx := logging.FromContextOrNew(ctx, nil) if lsv1alpha1helper.HasDeleteWithoutUninstallAnnotation(deployItem.ObjectMeta) { logger.Info("Deleting deployitem %s without uninstall", deployItem.Name) } else { - if err := c.deployer.Delete(ctx, lsCtx, deployItem, target); err != nil { + if err := c.deployer.Delete(ctx, lsCtx, deployItem, rt); err != nil { return lserrors.BuildLsError(err, "delete", "DeleteWithUninstall", err.Error()) } } diff --git a/pkg/deployer/lib/targetresolver/secret/secretrefresolver.go b/pkg/deployer/lib/targetresolver/secret/secretrefresolver.go new file mode 100644 index 000000000..2eb33f667 --- /dev/null +++ b/pkg/deployer/lib/targetresolver/secret/secretrefresolver.go @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package secret + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + . "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver" + lsutils "github.com/gardener/landscaper/pkg/utils" +) + +var _ TargetResolver = SecretRefResolver{} + +type SecretRefResolver struct { + Client client.Client +} + +func New(c client.Client) *SecretRefResolver { + return &SecretRefResolver{ + Client: c, + } +} + +func (srr SecretRefResolver) Resolve(ctx context.Context, target *lsv1alpha1.Target) (*lsv1alpha1.ResolvedTarget, error) { + rt := NewResolvedTarget(target) + + if target.Spec.SecretRef != nil { + sr := &lsv1alpha1.SecretReference{ + ObjectReference: lsv1alpha1.ObjectReference{ + Name: target.Spec.SecretRef.Name, + Namespace: target.Namespace, + }, + Key: target.Spec.SecretRef.Key, + } + + _, rawContent, _, err := lsutils.ResolveSecretReference(ctx, srr.Client, sr) + if err != nil { + return nil, err + } + rt.Content = string(rawContent) + } + + return rt, nil +} diff --git a/pkg/deployer/lib/targetresolver/targetresolver.go b/pkg/deployer/lib/targetresolver/targetresolver.go new file mode 100644 index 000000000..23585139a --- /dev/null +++ b/pkg/deployer/lib/targetresolver/targetresolver.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package targetresolver + +import ( + "context" + + lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" +) + +type TargetResolver interface { + // Resolve resolves the reference in the given Target, if possible. + Resolve(context.Context, *lsv1alpha1.Target) (*lsv1alpha1.ResolvedTarget, error) +} + +// NewResolvedTarget is a constructor for ResolvedTarget. +// It puts the target's inline configuration into the Content field, if the target doesn't contain a secret reference. +func NewResolvedTarget(target *lsv1alpha1.Target) *lsv1alpha1.ResolvedTarget { + res := &lsv1alpha1.ResolvedTarget{ + Target: target, + } + if target.Spec.SecretRef == nil && target.Spec.Configuration != nil { + res.Content = string(target.Spec.Configuration.RawMessage) + } + return res +} diff --git a/pkg/deployer/lib/utils.go b/pkg/deployer/lib/utils.go index b83348136..fbe5e4b6c 100644 --- a/pkg/deployer/lib/utils.go +++ b/pkg/deployer/lib/utils.go @@ -31,12 +31,13 @@ import ( "github.com/gardener/landscaper/pkg/api" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" ) // GetKubeconfigFromTargetConfig fetches the kubeconfig from a given config. // If the config defines the target from a secret that secret is read from all provided clients. -func GetKubeconfigFromTargetConfig(ctx context.Context, config *lsv1alpha1.KubernetesClusterTargetConfig, +func GetKubeconfigFromTargetConfig(ctx context.Context, config *targettypes.KubernetesClusterTargetConfig, targetNamespace string, lsClient client.Client) ([]byte, error) { if config.Kubeconfig.StrVal != nil { return []byte(*config.Kubeconfig.StrVal), nil @@ -65,7 +66,7 @@ func GetKubeconfigFromSecretRef(ctx context.Context, ref *lsv1alpha1.SecretRefer } if len(ref.Key) == 0 { - ref.Key = lsv1alpha1.DefaultKubeconfigKey + ref.Key = targettypes.DefaultKubeconfigKey } kubeconfig, ok := secret.Data[ref.Key] diff --git a/pkg/deployer/manifest/controller.go b/pkg/deployer/manifest/controller.go index 8d6299ebd..019e9d7f4 100644 --- a/pkg/deployer/manifest/controller.go +++ b/pkg/deployer/manifest/controller.go @@ -45,23 +45,23 @@ type deployer struct { hooks extension.ReconcileExtensionHooks } -func (d *deployer) Reconcile(ctx context.Context, _ *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { - manifest, err := New(d.lsClient, d.hostClient, &d.config, di, target) +func (d *deployer) Reconcile(ctx context.Context, _ *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + manifest, err := New(d.lsClient, d.hostClient, &d.config, di, rt) if err != nil { return err } return manifest.Reconcile(ctx) } -func (d deployer) Delete(ctx context.Context, _ *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { - manifest, err := New(d.lsClient, d.hostClient, &d.config, di, target) +func (d deployer) Delete(ctx context.Context, _ *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { + manifest, err := New(d.lsClient, d.hostClient, &d.config, di, rt) if err != nil { return err } return manifest.Delete(ctx) } -func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { +func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, rt *lsv1alpha1.ResolvedTarget) error { d.log.Info("abort is not yet implemented") return nil } diff --git a/pkg/deployer/manifest/export_test.go b/pkg/deployer/manifest/export_test.go index 19b9147db..ae4e5b7c5 100644 --- a/pkg/deployer/manifest/export_test.go +++ b/pkg/deployer/manifest/export_test.go @@ -21,6 +21,7 @@ import ( "github.com/gardener/landscaper/apis/deployer/utils/managedresource" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" + "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver" "github.com/gardener/landscaper/pkg/deployer/manifest" "github.com/gardener/landscaper/test/utils" "github.com/gardener/landscaper/test/utils/envtest" @@ -97,7 +98,7 @@ var _ = Describe("Export", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, targetresolver.NewResolvedTarget(target)) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -173,7 +174,7 @@ var _ = Describe("Export", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, targetresolver.NewResolvedTarget(target)) Expect(err).ToNot(HaveOccurred()) go func() { diff --git a/pkg/deployer/manifest/manifest.go b/pkg/deployer/manifest/manifest.go index 562f0d5ef..aebca8fb4 100644 --- a/pkg/deployer/manifest/manifest.go +++ b/pkg/deployer/manifest/manifest.go @@ -7,7 +7,6 @@ package manifest import ( "context" "encoding/base64" - "encoding/json" "errors" "fmt" @@ -23,7 +22,10 @@ import ( "github.com/gardener/landscaper/pkg/utils" + "k8s.io/apimachinery/pkg/util/yaml" + lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" manifestinstall "github.com/gardener/landscaper/apis/deployer/manifest/install" manifestv1alpha2 "github.com/gardener/landscaper/apis/deployer/manifest/v1alpha2" @@ -49,7 +51,7 @@ type Manifest struct { Configuration *manifestv1alpha2.Configuration DeployItem *lsv1alpha1.DeployItem - Target *lsv1alpha1.Target + Target *lsv1alpha1.ResolvedTarget ProviderConfiguration *manifestv1alpha2.ProviderConfiguration ProviderStatus *manifestv1alpha2.ProviderStatus @@ -68,7 +70,7 @@ func New(lsKubeClient client.Client, hostKubeClient client.Client, configuration *manifestv1alpha2.Configuration, item *lsv1alpha1.DeployItem, - target *lsv1alpha1.Target) (*Manifest, error) { + rt *lsv1alpha1.ResolvedTarget) (*Manifest, error) { config := &manifestv1alpha2.ProviderConfiguration{} currOp := "InitManifestOperation" @@ -97,7 +99,7 @@ func New(lsKubeClient client.Client, hostKubeClient: hostKubeClient, Configuration: configuration, DeployItem: item, - Target: target, + Target: rt, ProviderConfiguration: config, ProviderStatus: status, }, nil @@ -137,24 +139,14 @@ func (m *Manifest) TargetClient(ctx context.Context) (*rest.Config, client.Clien return restConfig, kubeClient, clientset, nil } if m.Target != nil { - var kubeconfigBytes []byte - var err error - - if m.Target.Spec.SecretRef != nil { - kubeconfigBytes, err = lib.GetKubeconfigFromSecretRef(ctx, m.Target.Spec.SecretRef, m.Target.Namespace, m.lsKubeClient) - if err != nil { - return nil, nil, nil, err - } - } else { - targetConfig := &lsv1alpha1.KubernetesClusterTargetConfig{} - if err = json.Unmarshal(m.Target.Spec.Configuration.RawMessage, targetConfig); err != nil { - return nil, nil, nil, fmt.Errorf("unable to parse target confíguration: %w", err) - } - - kubeconfigBytes, err = lib.GetKubeconfigFromTargetConfig(ctx, targetConfig, m.Target.Namespace, m.lsKubeClient) - if err != nil { - return nil, nil, nil, err - } + targetConfig := &targettypes.KubernetesClusterTargetConfig{} + if err := yaml.Unmarshal([]byte(m.Target.Content), targetConfig); err != nil { + return nil, nil, nil, fmt.Errorf("unable to parse target confíguration: %w", err) + } + + kubeconfigBytes, err := lib.GetKubeconfigFromTargetConfig(ctx, targetConfig, m.Target.Namespace, m.lsKubeClient) + if err != nil { + return nil, nil, nil, err } kubeconfig, err := clientcmd.NewClientConfigFromBytes(kubeconfigBytes) diff --git a/pkg/deployer/manifest/misc_test.go b/pkg/deployer/manifest/misc_test.go index 64e4a4c93..670db6e3f 100644 --- a/pkg/deployer/manifest/misc_test.go +++ b/pkg/deployer/manifest/misc_test.go @@ -20,11 +20,13 @@ import ( k8sclient "sigs.k8s.io/controller-runtime/pkg/client" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" manifestv1alpha2 "github.com/gardener/landscaper/apis/deployer/manifest/v1alpha2" "github.com/gardener/landscaper/apis/deployer/utils/managedresource" "github.com/gardener/landscaper/apis/deployer/utils/readinesschecks" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" + secretresolver "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver/secret" "github.com/gardener/landscaper/pkg/deployer/manifest" "github.com/gardener/landscaper/test/utils" "github.com/gardener/landscaper/test/utils/envtest" @@ -56,7 +58,7 @@ var _ = Describe("", func() { kSecret.Name = "my-target" kSecret.Namespace = state.Namespace kSecret.Data = map[string][]byte{ - lsv1alpha1.DefaultKubeconfigKey: kubeconfigBytes, + targettypes.DefaultKubeconfigKey: kubeconfigBytes, } Expect(state.Create(ctx, kSecret)).To(Succeed()) @@ -73,6 +75,10 @@ var _ = Describe("", func() { rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{} manifestConfig.Manifests = []managedresource.Manifest{ { @@ -88,7 +94,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -98,61 +104,6 @@ var _ = Describe("", func() { Expect(cmRes.Data).To(HaveKeyWithValue("key", "val")) }) - It("should fail if the secret ref has another namespace than the target", func() { - secretNamespace := state.Namespace + "alt" - secretNamespaceObj := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretNamespace, - }, - } - Expect(state.Create(ctx, secretNamespaceObj)).To(Succeed()) - - kubeconfigBytes, err := kutil.GenerateKubeconfigBytes(testenv.Env.Config) - Expect(err).ToNot(HaveOccurred()) - kSecret := &corev1.Secret{} - kSecret.Name = "my-target" - kSecret.Namespace = secretNamespace - kSecret.Data = map[string][]byte{ - lsv1alpha1.DefaultKubeconfigKey: kubeconfigBytes, - } - Expect(state.Create(ctx, kSecret)).To(Succeed()) - - target, err := utils.CreateKubernetesTargetFromSecret(state.Namespace, "my-target", kSecret) - Expect(err).ToNot(HaveOccurred()) - Expect(state.Create(ctx, target)).To(Succeed()) - - cm := &corev1.ConfigMap{} - cm.Name = "my-cm" - cm.Namespace = state.Namespace - cm.Data = map[string]string{ - "key": "val", - } - rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) - Expect(err).ToNot(HaveOccurred()) - - manifestConfig := &manifestv1alpha2.ProviderConfiguration{} - manifestConfig.Manifests = []managedresource.Manifest{ - { - Policy: managedresource.ManagePolicy, - Manifest: rawCM, - }, - } - item, err := manifest.NewDeployItemBuilder(). - Key(state.Namespace, "myitem"). - ProviderConfig(manifestConfig). - Target(target.Namespace, target.Name). - Build() - Expect(err).ToNot(HaveOccurred()) - Expect(state.Create(ctx, item)).To(Succeed()) - - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) - Expect(err).ToNot(HaveOccurred()) - - Expect(m.Reconcile(ctx)).NotTo(Succeed()) - - Expect(state.Client.Delete(ctx, secretNamespaceObj)).To(Succeed()) - }) - It("should add before delete annotations when manifest is being deleted", func() { target, err := utils.CreateKubernetesTarget(state.Namespace, "my-target", testenv.Env.Config) Expect(err).ToNot(HaveOccurred()) @@ -173,6 +124,10 @@ var _ = Describe("", func() { rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{} manifestConfig.Manifests = []managedresource.Manifest{ { @@ -191,7 +146,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -234,6 +189,10 @@ var _ = Describe("", func() { rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{} manifestConfig.Manifests = []managedresource.Manifest{ { @@ -252,7 +211,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -291,6 +250,10 @@ var _ = Describe("", func() { rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{} manifestConfig.Manifests = []managedresource.Manifest{ { @@ -309,7 +272,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -344,6 +307,10 @@ var _ = Describe("", func() { rawCM, err := kutil.ConvertToRawExtension(cm, scheme.Scheme) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{} manifestConfig.Manifests = []managedresource.Manifest{ { @@ -362,7 +329,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) Expect(m.Reconcile(ctx)).To(Succeed()) @@ -402,6 +369,10 @@ var _ = Describe("", func() { requirementValueMarshaled, err := json.Marshal(requirementValue) Expect(err).ToNot(HaveOccurred()) + sr := secretresolver.New(state.Client) + rt, err := sr.Resolve(ctx, target) + Expect(err).ToNot(HaveOccurred()) + manifestConfig := &manifestv1alpha2.ProviderConfiguration{ ReadinessChecks: readinesschecks.ReadinessCheckConfiguration{ Timeout: &lsv1alpha1.Duration{ @@ -452,7 +423,7 @@ var _ = Describe("", func() { Expect(err).ToNot(HaveOccurred()) Expect(state.Create(ctx, item)).To(Succeed()) - m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, target) + m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, item, rt) Expect(err).ToNot(HaveOccurred()) go func() { diff --git a/pkg/deployer/manifest/policy_test.go b/pkg/deployer/manifest/policy_test.go index 5e6c5f119..20d838122 100644 --- a/pkg/deployer/manifest/policy_test.go +++ b/pkg/deployer/manifest/policy_test.go @@ -20,6 +20,7 @@ import ( "github.com/gardener/landscaper/apis/deployer/utils/managedresource" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" + "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver" "github.com/gardener/landscaper/pkg/deployer/manifest" "github.com/gardener/landscaper/test/utils" "github.com/gardener/landscaper/test/utils/envtest" @@ -73,7 +74,7 @@ var _ = Describe("Policy", func() { ctx context.Context state *envtest.State configMap *corev1.ConfigMap - target *lsv1alpha1.Target + target *lsv1alpha1.ResolvedTarget ) BeforeEach(func() { @@ -83,9 +84,10 @@ var _ = Describe("Policy", func() { state, err = testenv.InitState(ctx) Expect(err).ToNot(HaveOccurred()) - target, err = utils.CreateKubernetesTarget(state.Namespace, "my-target", testenv.Env.Config) + rawTarget, err := utils.CreateKubernetesTarget(state.Namespace, "my-target", testenv.Env.Config) Expect(err).ToNot(HaveOccurred()) - Expect(state.Create(ctx, target)).To(Succeed()) + Expect(state.Create(ctx, rawTarget)).To(Succeed()) + target = targetresolver.NewResolvedTarget(rawTarget) configMap = &corev1.ConfigMap{} configMap.Name = "my-cm" @@ -102,7 +104,7 @@ var _ = Describe("Policy", func() { Context("Manage", func() { It("should create a configured configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ManagePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ManagePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -114,7 +116,7 @@ var _ = Describe("Policy", func() { }) It("should update the created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ManagePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ManagePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -137,7 +139,7 @@ var _ = Describe("Policy", func() { }) It("should delete a created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ManagePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ManagePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -156,7 +158,7 @@ var _ = Describe("Policy", func() { Context("Fallback", func() { It("should create a configured configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.FallbackPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.FallbackPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -168,7 +170,7 @@ var _ = Describe("Policy", func() { }) It("should update the created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.FallbackPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.FallbackPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -191,7 +193,7 @@ var _ = Describe("Policy", func() { }) It("should not update the created configmap when the deployer label is not matching", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.FallbackPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.FallbackPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -217,7 +219,7 @@ var _ = Describe("Policy", func() { }) It("should delete a created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.FallbackPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.FallbackPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -234,7 +236,7 @@ var _ = Describe("Policy", func() { }) It("should not delete a created configmap when the deployer label is not matching", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.FallbackPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.FallbackPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -255,7 +257,7 @@ var _ = Describe("Policy", func() { Context("Keep", func() { It("should create a configured configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.KeepPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.KeepPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -267,7 +269,7 @@ var _ = Describe("Policy", func() { }) It("should update the created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.KeepPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.KeepPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -290,7 +292,7 @@ var _ = Describe("Policy", func() { }) It("should not delete a created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.KeepPolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.KeepPolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -308,7 +310,7 @@ var _ = Describe("Policy", func() { Context("Ignore", func() { It("should not create a configured configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.IgnorePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.IgnorePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -321,7 +323,7 @@ var _ = Describe("Policy", func() { Context("Immutable", func() { It("should create a configured configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ImmutablePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ImmutablePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -333,7 +335,7 @@ var _ = Describe("Policy", func() { }) It("should not update the created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ImmutablePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ImmutablePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) @@ -356,7 +358,7 @@ var _ = Describe("Policy", func() { }) It("should delete a created configmap", func() { - deployItem := createDeployItem(ctx, state, "my-deployitem", target, configMap, managedresource.ImmutablePolicy) + deployItem := createDeployItem(ctx, state, "my-deployitem", target.Target, configMap, managedresource.ImmutablePolicy) m, err := manifest.New(testenv.Client, testenv.Client, &manifestv1alpha2.Configuration{}, deployItem, target) Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/deployer/manifest/readiness_check_test.go b/pkg/deployer/manifest/readiness_check_test.go index 79f405c80..a10a3be57 100644 --- a/pkg/deployer/manifest/readiness_check_test.go +++ b/pkg/deployer/manifest/readiness_check_test.go @@ -23,6 +23,7 @@ import ( "github.com/gardener/landscaper/apis/deployer/utils/readinesschecks" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" + "github.com/gardener/landscaper/pkg/deployer/lib/targetresolver" "github.com/gardener/landscaper/pkg/deployer/manifest" "github.com/gardener/landscaper/test/utils" "github.com/gardener/landscaper/test/utils/envtest" @@ -32,7 +33,7 @@ var _ = Describe("ReadinessCheck", func() { var ( ctx context.Context state *envtest.State - target *lsv1alpha1.Target + target *lsv1alpha1.ResolvedTarget ) BeforeEach(func() { @@ -42,9 +43,10 @@ var _ = Describe("ReadinessCheck", func() { state, err = testenv.InitState(ctx) Expect(err).ToNot(HaveOccurred()) - target, err = utils.CreateKubernetesTarget(state.Namespace, "my-target", testenv.Env.Config) + rawTarget, err := utils.CreateKubernetesTarget(state.Namespace, "my-target", testenv.Env.Config) Expect(err).ToNot(HaveOccurred()) - Expect(state.Create(ctx, target)).To(Succeed()) + Expect(state.Create(ctx, rawTarget)).To(Succeed()) + target = targetresolver.NewResolvedTarget(rawTarget) }) AfterEach(func() { diff --git a/pkg/deployer/mock/controller.go b/pkg/deployer/mock/controller.go index da150ad13..5036e8d39 100644 --- a/pkg/deployer/mock/controller.go +++ b/pkg/deployer/mock/controller.go @@ -54,7 +54,7 @@ type deployer struct { hooks extension.ReconcileExtensionHooks } -func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.Target) error { +func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.ResolvedTarget) error { config, err := d.getConfig(ctx, di) if err != nil { return err @@ -83,11 +83,11 @@ func (d *deployer) Reconcile(ctx context.Context, lsCtx *lsv1alpha1.Context, di return d.Writer().UpdateDeployItemStatus(ctx, read_write_layer.W000059, di) } -func (d *deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { +func (d *deployer) Delete(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.ResolvedTarget) error { return d.ensureDeletion(ctx, di) } -func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, target *lsv1alpha1.Target) error { +func (d *deployer) Abort(ctx context.Context, lsCtx *lsv1alpha1.Context, di *lsv1alpha1.DeployItem, _ *lsv1alpha1.ResolvedTarget) error { d.log.Info("abort is not yet implemented") return nil } diff --git a/pkg/landscaper/controllers/targetsync/targetsync_controller.go b/pkg/landscaper/controllers/targetsync/targetsync_controller.go index f9131b777..29c588aca 100644 --- a/pkg/landscaper/controllers/targetsync/targetsync_controller.go +++ b/pkg/landscaper/controllers/targetsync/targetsync_controller.go @@ -30,6 +30,7 @@ import ( "sigs.k8s.io/yaml" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" lserrors "github.com/gardener/landscaper/apis/errors" kutils "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" @@ -258,13 +259,10 @@ func (c *TargetSyncController) handleSecret(ctx context.Context, targetSync *lsv func (c *TargetSyncController) createOrUpdateTarget(ctx context.Context, targetSync *lsv1alpha1.TargetSync, secret *corev1.Secret) error { targetSpec := lsv1alpha1.TargetSpec{ - Type: lsv1alpha1.KubernetesClusterTargetType, - SecretRef: &lsv1alpha1.SecretReference{ - ObjectReference: lsv1alpha1.ObjectReference{ - Name: secret.Name, - Namespace: targetSync.Namespace, - }, - Key: lsv1alpha1.DefaultKubeconfigKey, + Type: targettypes.KubernetesClusterTargetType, + SecretRef: &lsv1alpha1.LocalSecretReference{ + Name: secret.Name, + Key: targettypes.DefaultKubeconfigKey, }, } diff --git a/pkg/landscaper/controllers/targetsync/targetsync_controller_test.go b/pkg/landscaper/controllers/targetsync/targetsync_controller_test.go index 0441a84ac..0c06a3b8c 100644 --- a/pkg/landscaper/controllers/targetsync/targetsync_controller_test.go +++ b/pkg/landscaper/controllers/targetsync/targetsync_controller_test.go @@ -18,6 +18,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/controller-utils/pkg/logging" testutils "github.com/gardener/landscaper/test/utils" @@ -64,8 +65,7 @@ var _ = Describe("TargetSync Controller", func() { testutils.ExpectNoError(state.Client.Get(ctx, kutil.ObjectKeyFromObject(secret), secret)) Expect(target.Spec.SecretRef.Name).To(Equal(secret.Name)) - Expect(target.Spec.SecretRef.Namespace).To(Equal(secret.Namespace)) - Expect(target.Spec.Type).To(Equal(lsv1alpha1.KubernetesClusterTargetType)) + Expect(target.Spec.Type).To(Equal(targettypes.KubernetesClusterTargetType)) Expect(secret.Data).To(Equal(sourceSecret.Data)) } diff --git a/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_environments.yaml b/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_environments.yaml index c27ed0390..4ff9f0832 100755 --- a/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_environments.yaml +++ b/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_environments.yaml @@ -60,10 +60,7 @@ spec: holds the data. type: string name: - description: Name is the name of the kubernetes object. - type: string - namespace: - description: Namespace is the namespace of kubernetes object. + description: Name is the name of the secret type: string required: - name diff --git a/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_targets.yaml b/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_targets.yaml index 0466efeaf..641e5cad3 100755 --- a/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_targets.yaml +++ b/pkg/landscaper/crdmanager/crdresources/landscaper.gardener.cloud_targets.yaml @@ -51,10 +51,7 @@ spec: the data. type: string name: - description: Name is the name of the kubernetes object. - type: string - namespace: - description: Namespace is the namespace of kubernetes object. + description: Name is the name of the secret type: string required: - name diff --git a/pkg/landscaper/dataobjects/target.go b/pkg/landscaper/dataobjects/target.go index a54f35e05..e48c45020 100644 --- a/pkg/landscaper/dataobjects/target.go +++ b/pkg/landscaper/dataobjects/target.go @@ -6,6 +6,7 @@ package dataobjects import ( "encoding/json" + "fmt" "strconv" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,8 +29,8 @@ type TargetExtension struct { // NewTargetExtension creates a new internal target instance from a raw target. func NewTargetExtension(target *lsv1alpha1.Target, targetImport *lsv1alpha1.TargetImport) *TargetExtension { metadata := Metadata{} - if target != nil && target.Spec.Configuration.RawMessage != nil { - metadata = GetMetadataFromObject(target, target.Spec.Configuration.RawMessage) + if target != nil { + metadata = GetMetadataFromObject(target, GetHashableContent(target)) } return &TargetExtension{ target: target, @@ -93,11 +94,26 @@ func (t *TargetExtension) Apply(target *lsv1alpha1.Target) error { kutil.SetMetaDataLabel(target, key, val) } tmpMetadata := t.metadata - tmpMetadata.Hash = generateHash(t.target.Spec.Configuration.RawMessage) + tmpMetadata.Hash = generateHash(GetHashableContent(t.target)) SetMetadataFromObject(target, tmpMetadata) return nil } +// GetHashableContent returns the value of the Target based on which its hash can be computed. +// This is either .Spec.Configuration.RawMessage or a json representation of .Spec.SecretRef. +// If neither is set (or the given target is nil), nil is returned. +func GetHashableContent(t *lsv1alpha1.Target) []byte { + if t == nil { + return nil + } + if t.Spec.Configuration != nil { + return t.Spec.Configuration.RawMessage + } else if t.Spec.SecretRef != nil { + return []byte(fmt.Sprintf(`{"secretRef": {"name": "%s", "key": "%s"}}`, t.Spec.SecretRef.Name, t.Spec.SecretRef.Key)) + } + return nil +} + // ApplyNameAndNamespace sets name and namespace based on the given metadata. func (t *TargetExtension) ApplyNameAndNamespace(target *lsv1alpha1.Target) { target.Name = lsv1alpha1helper.GenerateDataObjectName(t.metadata.Context, t.metadata.Key) diff --git a/pkg/landscaper/installations/exports/constructor.go b/pkg/landscaper/installations/exports/constructor.go index 4eeeb20d9..cfdea0629 100644 --- a/pkg/landscaper/installations/exports/constructor.go +++ b/pkg/landscaper/installations/exports/constructor.go @@ -198,7 +198,7 @@ func (c *Constructor) aggregateTargetsInContext(ctx context.Context) (map[string aggTargets := map[string]interface{}{} for _, target := range targetList.Items { - meta := dataobjects.GetMetadataFromObject(&target, target.Spec.Configuration.RawMessage) + meta := dataobjects.GetMetadataFromObject(&target, dataobjects.GetHashableContent(&target)) raw, err := json.Marshal(target) if err != nil { return nil, fmt.Errorf("error while encoding target %s: %w", target.Name, err) diff --git a/pkg/landscaper/installations/exports/constructor_test.go b/pkg/landscaper/installations/exports/constructor_test.go index a72484974..0bd0161c3 100644 --- a/pkg/landscaper/installations/exports/constructor_test.go +++ b/pkg/landscaper/installations/exports/constructor_test.go @@ -213,11 +213,11 @@ var _ = Describe("Constructor", func() { for _, next := range res { if next.GetMetadata().Key == "root.y" { Expect(next.GetTarget().Spec.Type).To(Equal(lsv1alpha1.TargetType("landscaper.gardener.cloud/mock"))) - Expect(next.GetTarget().Spec.Configuration).To(Equal(lsv1alpha1.AnyJSON{RawMessage: json.RawMessage(`"val-a"`)})) + Expect(next.GetTarget().Spec.Configuration).To(Equal(&lsv1alpha1.AnyJSON{RawMessage: json.RawMessage(`"val-a"`)})) Expect(next.GetMetadata().SourceType).To(Equal((lsv1alpha1.ExportDataObjectSourceType))) } else { Expect(next.GetTarget().Spec.Type).To(Equal(lsv1alpha1.TargetType("landscaper.gardener.cloud/mock"))) - Expect(next.GetTarget().Spec.Configuration).To(Equal(lsv1alpha1.AnyJSON{RawMessage: json.RawMessage(`"val-e"`)})) + Expect(next.GetTarget().Spec.Configuration).To(Equal(&lsv1alpha1.AnyJSON{RawMessage: json.RawMessage(`"val-e"`)})) Expect(next.GetMetadata().SourceType).To(Equal((lsv1alpha1.ExportDataObjectSourceType))) Expect(next.GetMetadata().Key).To(Equal(("root.z"))) } diff --git a/pkg/landscaper/installations/helper.go b/pkg/landscaper/installations/helper.go index 4f4dbb6e0..b8e8b0394 100644 --- a/pkg/landscaper/installations/helper.go +++ b/pkg/landscaper/installations/helper.go @@ -6,18 +6,14 @@ package installations import ( "context" - "encoding/json" "fmt" cdv2 "github.com/gardener/component-spec/bindings-go/apis/v2" "github.com/gardener/component-spec/bindings-go/ctf" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" @@ -31,6 +27,7 @@ import ( "github.com/gardener/landscaper/pkg/landscaper/dataobjects" "github.com/gardener/landscaper/pkg/landscaper/registry/componentoverwrites" "github.com/gardener/landscaper/pkg/landscaper/registry/components/cdutils" + lsutils "github.com/gardener/landscaper/pkg/utils" ) var componentInstallationGVK schema.GroupVersionKind @@ -134,7 +131,7 @@ func GetDataImport(ctx context.Context, kubeClient client.Client, contextName string, inst *InstallationAndImports, - dataImport lsv1alpha1.DataImport) (*dataobjects.DataObject, *v1.OwnerReference, error) { + dataImport lsv1alpha1.DataImport) (*dataobjects.DataObject, *metav1.OwnerReference, error) { var rawDataObject *lsv1alpha1.DataObject // get deploy item from current context @@ -146,7 +143,7 @@ func GetDataImport(ctx context.Context, } } if dataImport.SecretRef != nil { - _, data, gen, err := ResolveSecretReference(ctx, kubeClient, dataImport.SecretRef) + _, data, gen, err := lsutils.ResolveSecretReference(ctx, kubeClient, dataImport.SecretRef) if err != nil { return nil, nil, err } @@ -156,7 +153,7 @@ func GetDataImport(ctx context.Context, rawDataObject.SetGeneration(gen) } if dataImport.ConfigMapRef != nil { - _, data, gen, err := ResolveConfigMapReference(ctx, kubeClient, dataImport.ConfigMapRef) + _, data, gen, err := lsutils.ResolveConfigMapReference(ctx, kubeClient, dataImport.ConfigMapRef) if err != nil { return nil, nil, err } @@ -322,126 +319,6 @@ func GetReferenceFromComponentDescriptorDefinition(cdDef *lsv1alpha1.ComponentDe return cdDef.Reference } -// ByteMapToRawMessageMap converts a map of bytes to a map of json.RawMessages -func ByteMapToRawMessageMap(m map[string][]byte) (map[string]json.RawMessage, error) { - n := make(map[string]json.RawMessage, len(m)) - for key, val := range m { - jsonVal, err := yaml.ToJSON(val) - if err != nil { - return nil, err - } - n[key] = json.RawMessage(jsonVal) - } - return n, nil -} - -// StringMapToRawMessageMap converts a map of strings to a map of json.RawMessages -func StringMapToRawMessageMap(m map[string]string) (map[string]json.RawMessage, error) { - n := make(map[string]json.RawMessage, len(m)) - for key, val := range m { - jsonVal, err := yaml.ToJSON([]byte(val)) - if err != nil { - return nil, err - } - n[key] = json.RawMessage(jsonVal) - } - return n, nil -} - -// ResolveSecretReference is an auxiliary function that fetches the content of a secret as specified by the given SecretReference -// The first returned value is the complete secret content, the second one the specified key (if set), the third one is the generation of the secret -func ResolveSecretReference(ctx context.Context, kubeClient client.Client, secretRef *lsv1alpha1.SecretReference) (map[string][]byte, []byte, int64, error) { - secret := &corev1.Secret{} - if err := kubeClient.Get(ctx, secretRef.NamespacedName(), secret); err != nil { - return nil, nil, 0, err - } - completeData := secret.Data - var ( - data []byte - ok bool - rawMap map[string]json.RawMessage - err error - ) - if len(secretRef.Key) != 0 { - data, ok = secret.Data[secretRef.Key] - if !ok { - return nil, nil, 0, fmt.Errorf("key %s in secret %s does not exist", secretRef.Key, secretRef.NamespacedName().String()) - } - } else { - // use the whole secret as map - rawMap, err = ByteMapToRawMessageMap(secret.Data) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to convert secret data to raw message map: %w", err) - } - data, err = json.Marshal(rawMap) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to marshal secret data as map: %w", err) - } - } - - return completeData, data, secret.Generation, nil -} - -// ResolveConfigMapReference is an auxiliary function that fetches the content of a configmap as specified by the given ConfigMapReference -// The first returned value is the complete configmap content, the second one the specified key (if set), the third one is the generation of the configmap -func ResolveConfigMapReference(ctx context.Context, kubeClient client.Client, configMapRef *lsv1alpha1.ConfigMapReference) (map[string][]byte, []byte, int64, error) { - cm := &corev1.ConfigMap{} - if err := kubeClient.Get(ctx, configMapRef.NamespacedName(), cm); err != nil { - return nil, nil, 0, err - } - completeData := cm.BinaryData - if completeData == nil { - completeData = map[string][]byte{} - } - for k, v := range cm.Data { - // kubernetes verifies that this doesn't cause any collisions - completeData[k] = []byte(v) - } - var ( - data []byte - sdata string - rawMap map[string]json.RawMessage - err error - ) - keyFound := len(configMapRef.Key) == 0 - if cm.Data != nil { - if len(configMapRef.Key) != 0 { - sdata, keyFound = cm.Data[configMapRef.Key] - data = []byte(sdata) - } else { - // use whole configmap as json object - rawMap, err := StringMapToRawMessageMap(cm.Data) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to convert configmap data to raw message map: %w", err) - } - data, err = json.Marshal(rawMap) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to marshal configmap data as map: %w", err) - } - } - } - if cm.BinaryData != nil { - if len(configMapRef.Key) != 0 { - data, keyFound = cm.BinaryData[configMapRef.Key] - } else { - // use whole configmap as json object - rawMap, err = ByteMapToRawMessageMap(cm.BinaryData) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to convert configmap data to raw message map: %w", err) - } - data, err = json.Marshal(rawMap) - if err != nil { - return nil, nil, 0, fmt.Errorf("unable to marshal configmap data as map: %w", err) - } - } - } - if !keyFound { - return nil, nil, 0, fmt.Errorf("key '%s' in configmap '%s' does not exist", configMapRef.Key, configMapRef.NamespacedName().String()) - } - - return completeData, data, cm.Generation, nil -} - func OwnerReferenceIsInstallation(owner *metav1.OwnerReference) bool { return owner != nil && owner.Kind == "Installation" } diff --git a/pkg/landscaper/installations/operation_test.go b/pkg/landscaper/installations/operation_test.go index 3debc21e4..01b30cdba 100644 --- a/pkg/landscaper/installations/operation_test.go +++ b/pkg/landscaper/installations/operation_test.go @@ -57,7 +57,7 @@ var _ = Describe("Operation", func() { "lab": "val2", } target.Spec.Type = "test-type" - target.Spec.Configuration = lsv1alpha1.NewAnyJSON([]byte("true")) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer([]byte("true")) targetObj, err := utils.JSONSerializeToGenericObject(target) testutils.ExpectNoError(err) @@ -94,7 +94,7 @@ var _ = Describe("Operation", func() { "lab": "val4", } target.Spec.Type = "test-type-2" - target.Spec.Configuration = lsv1alpha1.NewAnyJSON([]byte("false")) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer([]byte("false")) targetObj, err = utils.JSONSerializeToGenericObject(target) testutils.ExpectNoError(err) op.Inst.SetImports(map[string]interface{}{ @@ -213,7 +213,7 @@ var _ = Describe("Operation", func() { "lab": "val2", } target.Spec.Type = "test-type" - target.Spec.Configuration = lsv1alpha1.NewAnyJSON([]byte("true")) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer([]byte("true")) targetExtension := dataobjects.NewTargetExtension(target, nil) targetExtensions := []*dataobjects.TargetExtension{ @@ -252,7 +252,7 @@ var _ = Describe("Operation", func() { "lab": "val4", } target.Spec.Type = "test-type-2" - target.Spec.Configuration = lsv1alpha1.NewAnyJSON([]byte("false")) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer([]byte("false")) targetExtension = dataobjects.NewTargetExtension(target, nil) targetExtensions = []*dataobjects.TargetExtension{ diff --git a/pkg/utils/builders.go b/pkg/utils/builders.go index 5e6d1287d..f1c9c798a 100644 --- a/pkg/utils/builders.go +++ b/pkg/utils/builders.go @@ -184,7 +184,7 @@ type TargetBuilder struct { Type string ObjectKey *lsv1alpha1.ObjectReference Configuration interface{} - Ref *lsv1alpha1.SecretReference + Ref *lsv1alpha1.LocalSecretReference annotations map[string]string } @@ -219,7 +219,7 @@ func (b *TargetBuilder) Config(obj interface{}) *TargetBuilder { return b } -func (b *TargetBuilder) SecretRef(secretRef *lsv1alpha1.SecretReference) *TargetBuilder { +func (b *TargetBuilder) SecretRef(secretRef *lsv1alpha1.LocalSecretReference) *TargetBuilder { b.Ref = secretRef return b } @@ -234,7 +234,7 @@ func (b *TargetBuilder) Build() (*lsv1alpha1.Target, error) { if err != nil { return nil, fmt.Errorf("unable to decode target config: %w", err) } - target.Spec.Configuration = lsv1alpha1.NewAnyJSON(configBytes) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer(configBytes) } if b.Ref != nil { diff --git a/pkg/utils/landscaper/installation_simulator_test.go b/pkg/utils/landscaper/installation_simulator_test.go index f2234e19d..1b5f09e08 100644 --- a/pkg/utils/landscaper/installation_simulator_test.go +++ b/pkg/utils/landscaper/installation_simulator_test.go @@ -20,6 +20,7 @@ import ( "sigs.k8s.io/yaml" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" "github.com/gardener/landscaper/pkg/landscaper/blueprints" componentsregistry "github.com/gardener/landscaper/pkg/landscaper/registry/components" lsutils "github.com/gardener/landscaper/pkg/utils/landscaper" @@ -171,8 +172,8 @@ targetExports: [] Namespace: "default", }, Spec: lsv1alpha1.TargetSpec{ - Type: lsv1alpha1.KubernetesClusterTargetType, - Configuration: lsv1alpha1.NewAnyJSON([]byte("{ \"kubeconfig\": \"{}\" }")), + Type: targettypes.KubernetesClusterTargetType, + Configuration: lsv1alpha1.NewAnyJSONPointer([]byte("{ \"kubeconfig\": \"{}\" }")), }, } @@ -221,7 +222,7 @@ targetExports: [] target := &lsv1alpha1.Target{} err = json.Unmarshal(marshalledTarget, target) Expect(err).ToNot(HaveOccurred()) - Expect(target.Spec.Type).To(Equal(lsv1alpha1.KubernetesClusterTargetType)) + Expect(target.Spec.Type).To(Equal(targettypes.KubernetesClusterTargetType)) Expect(target.Spec.Configuration).ToNot(BeNil()) Expect(callbacks.installations).To(HaveLen(4)) diff --git a/pkg/utils/landscaper/landscaper.go b/pkg/utils/landscaper/landscaper.go index 2d37bf401..dba281fb9 100644 --- a/pkg/utils/landscaper/landscaper.go +++ b/pkg/utils/landscaper/landscaper.go @@ -20,6 +20,7 @@ import ( lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" "github.com/gardener/landscaper/apis/core/v1alpha1/helper" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" kutil "github.com/gardener/landscaper/controller-utils/pkg/kubernetes" "github.com/gardener/landscaper/pkg/utils/read_write_layer" ) @@ -188,8 +189,8 @@ func BuildKubernetesTarget(target *lsv1alpha1.Target, restConfig *rest.Config) e return err } - config := lsv1alpha1.KubernetesClusterTargetConfig{ - Kubeconfig: lsv1alpha1.ValueRef{ + config := targettypes.KubernetesClusterTargetConfig{ + Kubeconfig: targettypes.ValueRef{ StrVal: pointer.StringPtr(string(data)), }, } @@ -198,8 +199,8 @@ func BuildKubernetesTarget(target *lsv1alpha1.Target, restConfig *rest.Config) e return err } - target.Spec.Type = lsv1alpha1.KubernetesClusterTargetType - target.Spec.Configuration = lsv1alpha1.NewAnyJSON(data) + target.Spec.Type = targettypes.KubernetesClusterTargetType + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer(data) return nil } diff --git a/pkg/utils/references.go b/pkg/utils/references.go new file mode 100644 index 000000000..8d323c577 --- /dev/null +++ b/pkg/utils/references.go @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "encoding/json" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/controller-runtime/pkg/client" + + lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" +) + +// ByteMapToRawMessageMap converts a map of bytes to a map of json.RawMessages +func ByteMapToRawMessageMap(m map[string][]byte) (map[string]json.RawMessage, error) { + n := make(map[string]json.RawMessage, len(m)) + for key, val := range m { + jsonVal, err := yaml.ToJSON(val) + if err != nil { + return nil, err + } + n[key] = json.RawMessage(jsonVal) + } + return n, nil +} + +// StringMapToRawMessageMap converts a map of strings to a map of json.RawMessages +func StringMapToRawMessageMap(m map[string]string) (map[string]json.RawMessage, error) { + n := make(map[string]json.RawMessage, len(m)) + for key, val := range m { + jsonVal, err := yaml.ToJSON([]byte(val)) + if err != nil { + return nil, err + } + n[key] = json.RawMessage(jsonVal) + } + return n, nil +} + +// ResolveSecretReference is an auxiliary function that fetches the content of a secret as specified by the given SecretReference +// The first returned value is the complete secret content, the second one the specified key (if set), the third one is the generation of the secret +func ResolveSecretReference(ctx context.Context, kubeClient client.Client, secretRef *lsv1alpha1.SecretReference) (map[string][]byte, []byte, int64, error) { + secret := &corev1.Secret{} + if err := kubeClient.Get(ctx, secretRef.NamespacedName(), secret); err != nil { + return nil, nil, 0, err + } + completeData := secret.Data + var ( + data []byte + ok bool + rawMap map[string]json.RawMessage + err error + ) + if len(secretRef.Key) != 0 { + data, ok = secret.Data[secretRef.Key] + if !ok { + return nil, nil, 0, fmt.Errorf("key %s in secret %s does not exist", secretRef.Key, secretRef.NamespacedName().String()) + } + } else { + // use the whole secret as map + rawMap, err = ByteMapToRawMessageMap(secret.Data) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to convert secret data to raw message map: %w", err) + } + data, err = json.Marshal(rawMap) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to marshal secret data as map: %w", err) + } + } + + return completeData, data, secret.Generation, nil +} + +// ResolveConfigMapReference is an auxiliary function that fetches the content of a configmap as specified by the given ConfigMapReference +// The first returned value is the complete configmap content, the second one the specified key (if set), the third one is the generation of the configmap +func ResolveConfigMapReference(ctx context.Context, kubeClient client.Client, configMapRef *lsv1alpha1.ConfigMapReference) (map[string][]byte, []byte, int64, error) { + cm := &corev1.ConfigMap{} + if err := kubeClient.Get(ctx, configMapRef.NamespacedName(), cm); err != nil { + return nil, nil, 0, err + } + completeData := cm.BinaryData + if completeData == nil { + completeData = map[string][]byte{} + } + for k, v := range cm.Data { + // kubernetes verifies that this doesn't cause any collisions + completeData[k] = []byte(v) + } + var ( + data []byte + sdata string + rawMap map[string]json.RawMessage + err error + ) + keyFound := len(configMapRef.Key) == 0 + if cm.Data != nil { + if len(configMapRef.Key) != 0 { + sdata, keyFound = cm.Data[configMapRef.Key] + data = []byte(sdata) + } else { + // use whole configmap as json object + rawMap, err := StringMapToRawMessageMap(cm.Data) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to convert configmap data to raw message map: %w", err) + } + data, err = json.Marshal(rawMap) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to marshal configmap data as map: %w", err) + } + } + } + if cm.BinaryData != nil { + if len(configMapRef.Key) != 0 { + data, keyFound = cm.BinaryData[configMapRef.Key] + } else { + // use whole configmap as json object + rawMap, err = ByteMapToRawMessageMap(cm.BinaryData) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to convert configmap data to raw message map: %w", err) + } + data, err = json.Marshal(rawMap) + if err != nil { + return nil, nil, 0, fmt.Errorf("unable to marshal configmap data as map: %w", err) + } + } + } + if !keyFound { + return nil, nil, 0, fmt.Errorf("key '%s' in configmap '%s' does not exist", configMapRef.Key, configMapRef.NamespacedName().String()) + } + + return completeData, data, cm.Generation, nil +} diff --git a/pkg/utils/utils_suite_test.go b/pkg/utils/utils_suite_test.go index 62e5e1b48..f03685aa3 100644 --- a/pkg/utils/utils_suite_test.go +++ b/pkg/utils/utils_suite_test.go @@ -5,13 +5,106 @@ package utils_test import ( + "context" + "encoding/json" "testing" + corev1 "k8s.io/api/core/v1" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/util/yaml" + + lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + lsutils "github.com/gardener/landscaper/pkg/utils" + "github.com/gardener/landscaper/test/utils" + "github.com/gardener/landscaper/test/utils/envtest" ) func TestConfig(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Utils Test Suite") } + +var _ = Describe("Utils", func() { + + Context("References", func() { + + var ( + ctx context.Context + state *envtest.State + ) + + BeforeEach(func() { + ctx = context.Background() + var err error + _, state, err = envtest.NewFakeClientFromPath("") + utils.ExpectNoError(err) + ns := &corev1.Namespace{} + ns.GenerateName = "tests-" + utils.ExpectNoError(state.Create(ctx, ns)) + state.Namespace = ns.Name + }) + + AfterEach(func() { + defer ctx.Done() + }) + + It("should correctly resolve secret references with and without key", func() { + type MyStruct struct { + Foo lsv1alpha1.NamedObjectReference `json:"foo"` + } + key := "foo" + + var err error + kSecret := &corev1.Secret{} + kSecret.Name = "my-secret" + kSecret.Namespace = state.Namespace + kSecret.Data = map[string][]byte{} + kSecret.Data[key], err = yaml.ToJSON([]byte(`name: foo +ref: + name: bar + namespace: baz`)) + utils.ExpectNoError(err) + Expect(state.Create(ctx, kSecret)).To(Succeed()) + + By("secret reference with key") + sr1 := &lsv1alpha1.SecretReference{ + ObjectReference: lsv1alpha1.ObjectReference{ + Name: kSecret.Name, + Namespace: kSecret.Namespace, + }, + Key: key, + } + + wholeSecret1, value1, gen1, err := lsutils.ResolveSecretReference(ctx, state.Client, sr1) + utils.ExpectNoError(err) + + ms1 := &MyStruct{ + Foo: lsv1alpha1.NamedObjectReference{}, + } + utils.ExpectNoError(json.Unmarshal(value1, &ms1.Foo)) + + By("secret reference without key") + sr2 := &lsv1alpha1.SecretReference{ + ObjectReference: lsv1alpha1.ObjectReference{ + Name: kSecret.Name, + Namespace: kSecret.Namespace, + }, + } + + wholeSecret2, value2, gen2, err := lsutils.ResolveSecretReference(ctx, state.Client, sr2) + utils.ExpectNoError(err) + + ms2 := &MyStruct{} + utils.ExpectNoError(json.Unmarshal(value2, ms2)) + + Expect(wholeSecret2).To(BeEquivalentTo(wholeSecret1), "the whole returned secret content should be equivalent, independent of whether the reference contains a key or not") + Expect(gen2).To(Equal(gen1), "the returned secret generation should be equivalent, independent of whether the reference contains a key or not") + Expect(ms2).To(Equal(ms1), "unmarshalled objects should be identical, independent of whether the reference contains a key or not") + Expect(ms2.Foo.Reference.Name).To(BeEquivalentTo("bar")) + }) + + }) + +}) diff --git a/pkg/utils/webhook/webhook.go b/pkg/utils/webhook/webhook.go index ecd2ead06..8c814fa06 100644 --- a/pkg/utils/webhook/webhook.go +++ b/pkg/utils/webhook/webhook.go @@ -28,13 +28,16 @@ import ( func ValidatorFromResourceType(log logging.Logger, kubeClient client.Client, scheme *runtime.Scheme, resource string) (GenericValidator, error) { abstrVal := newAbstractedValidator(log, kubeClient, scheme) var val GenericValidator - if resource == "installations" { + switch resource { + case "installations": val = &InstallationValidator{abstrVal} - } else if resource == "deployitems" { + case "deployitems": val = &DeployItemValidator{abstrVal} - } else if resource == "executions" { + case "executions": val = &ExecutionValidator{abstrVal} - } else { + case "targets": + val = &TargetValidator{abstrVal} + default: return nil, fmt.Errorf("unable to find validator for resource type %q", resource) } return val, nil @@ -188,3 +191,38 @@ func (ev *ExecutionValidator) handlePrivate(ctx context.Context, req admission.R return admission.Allowed("Execution is valid") } + +// TARGET + +// TargetValidator represents a validator for a Target +type TargetValidator struct{ abstractValidator } + +// Handle handles a request to the webhook +func (tv *TargetValidator) Handle(ctx context.Context, req admission.Request) admission.Response { + logger := tv.log.WithValues(lc.KeyResourceGroup, req.Kind.Group, lc.KeyResourceKind, req.Kind.Kind, lc.KeyResourceVersion, req.Kind.Version) + ctx = logging.NewContext(ctx, logger) + + timeBefore := time.Now() + result := tv.handlePrivate(ctx, req) + + logIfDurationExceeded(ctx, timeBefore) + + return result +} + +func (tv *TargetValidator) handlePrivate(ctx context.Context, req admission.Request) admission.Response { + logger, _ := logging.FromContextOrNew(ctx, []interface{}{lc.KeyMethod, "TargetValidator.handlePrivate"}) + + logger.Debug("Received request") + + t := &lscore.Target{} + if _, _, err := tv.decoder.Decode(req.Object.Raw, nil, t); err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + if errs := validation.ValidateTarget(t); len(errs) > 0 { + return admission.Denied(errs.ToAggregate().Error()) + } + + return admission.Allowed("Target is valid") +} diff --git a/test/integration/deployers/container/containertests.go b/test/integration/deployers/container/containertests.go index 84c66de82..88a1bcf44 100644 --- a/test/integration/deployers/container/containertests.go +++ b/test/integration/deployers/container/containertests.go @@ -17,12 +17,17 @@ import ( v1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + lsv1alpha1helper "github.com/gardener/landscaper/apis/core/v1alpha1/helper" + containerv1alpha1 "github.com/gardener/landscaper/apis/deployer/container/v1alpha1" + "github.com/gardener/landscaper/pkg/deployer/container" lsutils "github.com/gardener/landscaper/pkg/utils/landscaper" "github.com/gardener/landscaper/test/utils" @@ -237,4 +242,165 @@ func ContainerTests(f *framework.Framework) { Expect(stateData).To(HaveKey("count")) Expect(stateData["count"]).To(BeEquivalentTo(2)) }) + + Context("Targets", func() { + + var ( + cdi *lsv1alpha1.DeployItem + targetName = "test-target" + secretName = "test-secret" + targetContent = []byte(`{"foo": "bar"}`) + expectedTargetContentAsObject interface{} + ) + + BeforeEach(func() { + utils.ExpectNoError(json.Unmarshal(targetContent, &expectedTargetContentAsObject)) + conf := containerv1alpha1.ProviderConfiguration{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "container.deployer.landscaper.gardener.cloud/v1alpha1", + Kind: "ProviderConfiguration", + }, + Image: "alpine", + Command: []string{"/bin/sh"}, + Args: []string{"-c", "cp $TARGET_PATH $EXPORTS_PATH"}, + } + rawConf, err := json.Marshal(conf) + utils.ExpectNoError(err) + + cdi = &lsv1alpha1.DeployItem{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-", + Namespace: state.Namespace, + }, + Spec: lsv1alpha1.DeployItemSpec{ + Type: container.Type, + Target: &lsv1alpha1.ObjectReference{ + Name: targetName, + Namespace: state.Namespace, + }, + Configuration: &runtime.RawExtension{ + Raw: rawConf, + }, + }, + } + lsv1alpha1helper.SetOperation(&cdi.ObjectMeta, lsv1alpha1.TestReconcileOperation) + }) + + It("should make the Target content available inside the container - inline configuration", func() { + target := &lsv1alpha1.Target{ + ObjectMeta: metav1.ObjectMeta{ + Name: targetName, + Namespace: state.Namespace, + }, + Spec: lsv1alpha1.TargetSpec{ + Type: "landscaper.gardener.cloud/test", + Configuration: lsv1alpha1.NewAnyJSONPointer(targetContent), + }, + } + utils.ExpectNoError(state.Create(ctx, target)) + utils.ExpectNoError(state.Client.Get(ctx, client.ObjectKeyFromObject(target), target)) + + utils.ExpectNoError(state.Create(ctx, cdi)) + utils.ExpectNoError(lsutils.WaitForDeployItemToFinish(ctx, state.Client, cdi, lsv1alpha1.DeployItemPhaseSucceeded, 3*time.Minute)) + + exportSecret := &v1.Secret{} + utils.ExpectNoError(state.Client.Get(ctx, cdi.Status.ExportReference.NamespacedName(), exportSecret)) + Expect(exportSecret.Data).To(HaveKey(lsv1alpha1.DataObjectSecretDataKey)) + rawResolvedTarget := exportSecret.Data[lsv1alpha1.DataObjectSecretDataKey] + rt := &lsv1alpha1.ResolvedTarget{} + utils.ExpectNoError(json.Unmarshal(rawResolvedTarget, rt)) + Expect(rt.Target).To(Equal(target)) + var actualTargetContentAsObject interface{} + utils.ExpectNoError(json.Unmarshal([]byte(rt.Content), &actualTargetContentAsObject)) + Expect(actualTargetContentAsObject).To(Equal(expectedTargetContentAsObject)) + }) + + It("should make the Target content available inside the container - secretRef with key", func() { + secretKey := "key" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: state.Namespace, + }, + Data: map[string][]byte{ + secretKey: targetContent, + }, + } + utils.ExpectNoError(state.Create(ctx, secret)) + target := &lsv1alpha1.Target{ + ObjectMeta: metav1.ObjectMeta{ + Name: targetName, + Namespace: state.Namespace, + }, + Spec: lsv1alpha1.TargetSpec{ + Type: "landscaper.gardener.cloud/test", + SecretRef: &lsv1alpha1.LocalSecretReference{ + Name: secretName, + Key: secretKey, + }, + }, + } + utils.ExpectNoError(state.Create(ctx, target)) + utils.ExpectNoError(state.Client.Get(ctx, client.ObjectKeyFromObject(target), target)) + + utils.ExpectNoError(state.Create(ctx, cdi)) + utils.ExpectNoError(lsutils.WaitForDeployItemToFinish(ctx, state.Client, cdi, lsv1alpha1.DeployItemPhaseSucceeded, 3*time.Minute)) + + exportSecret := &v1.Secret{} + utils.ExpectNoError(state.Client.Get(ctx, cdi.Status.ExportReference.NamespacedName(), exportSecret)) + Expect(exportSecret.Data).To(HaveKey(lsv1alpha1.DataObjectSecretDataKey)) + rawResolvedTarget := exportSecret.Data[lsv1alpha1.DataObjectSecretDataKey] + rt := &lsv1alpha1.ResolvedTarget{} + utils.ExpectNoError(json.Unmarshal(rawResolvedTarget, rt)) + Expect(rt.Target).To(Equal(target)) + var actualTargetContentAsObject interface{} + utils.ExpectNoError(json.Unmarshal([]byte(rt.Content), &actualTargetContentAsObject)) + Expect(actualTargetContentAsObject).To(Equal(expectedTargetContentAsObject)) + }) + + It("should make the Target content available inside the container - secretRef without key", func() { + targetContentAsStringMap := map[string]string{} + utils.ExpectNoError(json.Unmarshal(targetContent, &targetContentAsStringMap)) + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: state.Namespace, + }, + Data: map[string][]byte{}, + } + for k, v := range targetContentAsStringMap { + secret.Data[k] = []byte(v) + } + utils.ExpectNoError(state.Create(ctx, secret)) + target := &lsv1alpha1.Target{ + ObjectMeta: metav1.ObjectMeta{ + Name: targetName, + Namespace: state.Namespace, + }, + Spec: lsv1alpha1.TargetSpec{ + Type: "landscaper.gardener.cloud/test", + SecretRef: &lsv1alpha1.LocalSecretReference{ + Name: secretName, + }, + }, + } + utils.ExpectNoError(state.Create(ctx, target)) + utils.ExpectNoError(state.Client.Get(ctx, client.ObjectKeyFromObject(target), target)) + + utils.ExpectNoError(state.Create(ctx, cdi)) + utils.ExpectNoError(lsutils.WaitForDeployItemToFinish(ctx, state.Client, cdi, lsv1alpha1.DeployItemPhaseSucceeded, 3*time.Minute)) + + exportSecret := &v1.Secret{} + utils.ExpectNoError(state.Client.Get(ctx, cdi.Status.ExportReference.NamespacedName(), exportSecret)) + Expect(exportSecret.Data).To(HaveKey(lsv1alpha1.DataObjectSecretDataKey)) + rawResolvedTarget := exportSecret.Data[lsv1alpha1.DataObjectSecretDataKey] + rt := &lsv1alpha1.ResolvedTarget{} + utils.ExpectNoError(json.Unmarshal(rawResolvedTarget, rt)) + Expect(rt.Target).To(Equal(target)) + var actualTargetContentAsObject interface{} + utils.ExpectNoError(json.Unmarshal([]byte(rt.Content), &actualTargetContentAsObject)) + Expect(actualTargetContentAsObject).To(Equal(expectedTargetContentAsObject)) + }) + + }) } diff --git a/test/integration/deployitems/timeouts.go b/test/integration/deployitems/timeouts.go index 844aad929..8995ce8ed 100644 --- a/test/integration/deployitems/timeouts.go +++ b/test/integration/deployitems/timeouts.go @@ -32,9 +32,9 @@ func RegisterTests(f *framework.Framework) { const ( waitingForDeployItems = 5 * time.Second // how long to wait for the landscaper to create deploy items from the installation - deployItemPickupTimeout = 10 * time.Second // the landscaper has to be configured accordingly for this test to work! - deployItemAbortingTimeout = 10 * time.Second // the landscaper has to be configured accordingly for this test to work! - waitingForReconcile = 10 * time.Second // how long to wait for the landscaper or the deployer to reconcile and update the deploy item + deployItemPickupTimeout = 30 * time.Second // the landscaper has to be configured accordingly for this test to work! + deployItemAbortingTimeout = 30 * time.Second // the landscaper has to be configured accordingly for this test to work! + waitingForReconcile = 30 * time.Second // how long to wait for the landscaper or the deployer to reconcile and update the deploy item resyncTime = 1 * time.Second // after which time to check again if the condition was not fulfilled the last time ) diff --git a/test/integration/suite_test.go b/test/integration/suite_test.go index d51b6b01b..7dac52e19 100644 --- a/test/integration/suite_test.go +++ b/test/integration/suite_test.go @@ -11,6 +11,7 @@ import ( "github.com/gardener/landscaper/test/integration/core" "github.com/gardener/landscaper/test/integration/dependencies" + "github.com/gardener/landscaper/test/integration/deployers" "github.com/gardener/landscaper/test/integration/deployitems" "github.com/gardener/landscaper/test/integration/executions" "github.com/gardener/landscaper/test/integration/importexport" @@ -27,7 +28,6 @@ import ( "github.com/gardener/landscaper/hack/testcluster/pkg/utils" "github.com/gardener/landscaper/test/framework" - "github.com/gardener/landscaper/test/integration/deployers" ) var opts *framework.Options diff --git a/test/integration/targets/targets.go b/test/integration/targets/targets.go index 1f6259a62..a1e2e06b5 100644 --- a/test/integration/targets/targets.go +++ b/test/integration/targets/targets.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" lsv1alpha1 "github.com/gardener/landscaper/apis/core/v1alpha1" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" lsutils "github.com/gardener/landscaper/pkg/utils/landscaper" "github.com/gardener/landscaper/test/framework" "github.com/gardener/landscaper/test/utils" @@ -67,7 +68,7 @@ func TargetTests(f *framework.Framework) { targetKey := client.ObjectKey{Namespace: state.Namespace, Name: "target-1"} target = &lsv1alpha1.Target{} utils.ExpectNoError(f.Client.Get(ctx, targetKey, target)) - targetConfig := &lsv1alpha1.KubernetesClusterTargetConfig{} + targetConfig := &targettypes.KubernetesClusterTargetConfig{} utils.GetTargetConfiguration(target, targetConfig) Expect(*targetConfig.Kubeconfig.StrVal).To(Equal("dummy kubeconfig 1")) @@ -91,7 +92,7 @@ func TargetTests(f *framework.Framework) { targetKey = client.ObjectKey{Namespace: state.Namespace, Name: "target-2"} target = &lsv1alpha1.Target{} utils.ExpectNoError(f.Client.Get(ctx, targetKey, target)) - targetConfig = &lsv1alpha1.KubernetesClusterTargetConfig{} + targetConfig = &targettypes.KubernetesClusterTargetConfig{} utils.GetTargetConfiguration(target, &targetConfig) Expect(*targetConfig.Kubeconfig.StrVal).To(Equal("dummy kubeconfig 2")) @@ -334,7 +335,7 @@ func TargetTests(f *framework.Framework) { targetKey := client.ObjectKey{Namespace: state.Namespace, Name: "target-1"} target = &lsv1alpha1.Target{} utils.ExpectNoError(f.Client.Get(ctx, targetKey, target)) - targetConfig := &lsv1alpha1.KubernetesClusterTargetConfig{} + targetConfig := &targettypes.KubernetesClusterTargetConfig{} utils.GetTargetConfiguration(target, targetConfig) Expect(*targetConfig.Kubeconfig.StrVal).To(Equal("dummy kubeconfig")) @@ -352,7 +353,7 @@ func TargetTests(f *framework.Framework) { By("Check exported Target") target = &lsv1alpha1.Target{} utils.ExpectNoError(f.Client.Get(ctx, targetKey, target)) - targetConfig = &lsv1alpha1.KubernetesClusterTargetConfig{} + targetConfig = &targettypes.KubernetesClusterTargetConfig{} utils.GetTargetConfiguration(target, targetConfig) Expect(*targetConfig.Kubeconfig.StrVal).To(Equal("dummy kubeconfig modified")) }) diff --git a/test/integration/webhook/webhook.go b/test/integration/webhook/webhook.go index 42ef5d23f..28899c545 100644 --- a/test/integration/webhook/webhook.go +++ b/test/integration/webhook/webhook.go @@ -146,5 +146,26 @@ func WebhookTest(f *framework.Framework) { Expect(err).To(HaveOccurred()) // validation webhook should have denied this Expect(err.Error()).To(HavePrefix("admission webhook \"deployitems.validation.landscaper.gardener.cloud\" denied the request")) }) + + It("should block invalid Target resources", func() { + // create invalid target (config and secretRef set) + target := &lsv1alpha1.Target{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-target", + Namespace: state.Namespace, + }, + Spec: lsv1alpha1.TargetSpec{ + Type: "landscaper.gardener.cloud/test", + Configuration: lsv1alpha1.NewAnyJSONPointer([]byte(`{"foo": "bar"}`)), + SecretRef: &lsv1alpha1.LocalSecretReference{ + Name: "foo", + }, + }, + } + + err := state.Create(ctx, target) + Expect(err).To(HaveOccurred()) // validation webhook should have denied this + Expect(err.Error()).To(HavePrefix("admission webhook \"targets.validation.landscaper.gardener.cloud\" denied the request")) + }) }) } diff --git a/test/utils/resources.go b/test/utils/resources.go index 188375cc4..7df592c72 100644 --- a/test/utils/resources.go +++ b/test/utils/resources.go @@ -13,8 +13,10 @@ import ( "path/filepath" lsv1alpha1helper "github.com/gardener/landscaper/apis/core/v1alpha1/helper" + "github.com/gardener/landscaper/apis/core/v1alpha1/targettypes" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/pointer" cdv2 "github.com/gardener/component-spec/bindings-go/apis/v2" "github.com/mandelsoft/vfs/pkg/osfs" @@ -24,7 +26,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" - "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -138,7 +139,7 @@ func CreateOrUpdateTarget(ctx context.Context, client client.Client, namespace, _, err = controllerutil.CreateOrUpdate(ctx, client, target, func() error { target.Spec.Type = lsv1alpha1.TargetType(ttype) - target.Spec.Configuration = lsv1alpha1.NewAnyJSON(data) + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer(data) return nil }) if err != nil { @@ -154,8 +155,8 @@ func CreateKubernetesTarget(namespace, name string, restConfig *rest.Config) (*l return nil, err } - config := lsv1alpha1.KubernetesClusterTargetConfig{ - Kubeconfig: lsv1alpha1.ValueRef{ + config := targettypes.KubernetesClusterTargetConfig{ + Kubeconfig: targettypes.ValueRef{ StrVal: pointer.StringPtr(string(data)), }, } @@ -168,8 +169,8 @@ func CreateKubernetesTarget(namespace, name string, restConfig *rest.Config) (*l target.Name = name target.Namespace = namespace - target.Spec.Type = lsv1alpha1.KubernetesClusterTargetType - target.Spec.Configuration = lsv1alpha1.NewAnyJSON(data) + target.Spec.Type = targettypes.KubernetesClusterTargetType + target.Spec.Configuration = lsv1alpha1.NewAnyJSONPointer(data) return target, nil } @@ -187,13 +188,12 @@ func CreateKubernetesTargetFromSecret(namespace, name string, secret *corev1.Sec target.Name = name target.Namespace = namespace - target.Spec.Type = lsv1alpha1.KubernetesClusterTargetType - target.Spec.SecretRef = &lsv1alpha1.SecretReference{ - ObjectReference: lsv1alpha1.ObjectReference{ - Name: secret.Name, - Namespace: secret.Namespace, - }, - Key: key, + target.Spec.Type = targettypes.KubernetesClusterTargetType + target.Spec.SecretRef = &lsv1alpha1.LocalSecretReference{ + Name: secret.Name, + } + if key != targettypes.DefaultKubeconfigKey { + target.Spec.SecretRef.Key = key } return target, nil @@ -235,7 +235,7 @@ func BuildInternalKubernetesTarget(ctx context.Context, kubeClient client.Client func BuildTargetAndSecretFromKubernetesTarget(target *lsv1alpha1.Target) (*lsv1alpha1.Target, *corev1.Secret, error) { const key = "kubeconfig" - config := lsv1alpha1.KubernetesClusterTargetConfig{} + config := targettypes.KubernetesClusterTargetConfig{} if err := json.Unmarshal(target.Spec.Configuration.RawMessage, &config); err != nil { return nil, nil, err } @@ -354,9 +354,9 @@ func ReadAndCreateOrUpdateDeployItem(ctx context.Context, testenv *envtest.Envir testenv.Client, di.Spec.Target.Namespace, di.Spec.Target.Name, - string(lsv1alpha1.KubernetesClusterTargetType), - lsv1alpha1.KubernetesClusterTargetConfig{ - Kubeconfig: lsv1alpha1.ValueRef{ + string(targettypes.KubernetesClusterTargetType), + targettypes.KubernetesClusterTargetConfig{ + Kubeconfig: targettypes.ValueRef{ StrVal: pointer.StringPtr(string(kubeconfigBytes)), }, }, diff --git a/vendor/github.com/gardener/landscaper/apis/core/types_shared.go b/vendor/github.com/gardener/landscaper/apis/core/types_shared.go index e28395740..e941efe66 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/types_shared.go +++ b/vendor/github.com/gardener/landscaper/apis/core/types_shared.go @@ -85,6 +85,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/vendor/github.com/gardener/landscaper/apis/core/types_target.go b/vendor/github.com/gardener/landscaper/apis/core/types_target.go index b42bc7016..5756f912f 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/types_target.go +++ b/vendor/github.com/gardener/landscaper/apis/core/types_target.go @@ -41,12 +41,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -68,3 +68,14 @@ type TargetTemplate struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` } + +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` + + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` +} diff --git a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/targettypes/kubernetes_cluster.go b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/targettypes/kubernetes_cluster.go new file mode 100644 index 000000000..d4fcae034 --- /dev/null +++ b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/targettypes/kubernetes_cluster.go @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package targettypes + +import ( + "encoding/json" + + "k8s.io/utils/pointer" + + "github.com/gardener/landscaper/apis/core" + "github.com/gardener/landscaper/apis/core/v1alpha1" +) + +// KubernetesClusterTargetType defines the landscaper kubernetes cluster target. +const KubernetesClusterTargetType v1alpha1.TargetType = core.GroupName + "/kubernetes-cluster" + +// KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config. +type KubernetesClusterTargetConfig struct { + // Kubeconfig defines kubeconfig as string. + Kubeconfig ValueRef `json:"kubeconfig"` +} + +// DefaultKubeconfigKey is the default that is used to hold a kubeconfig. +const DefaultKubeconfigKey = "kubeconfig" + +// ValueRef holds a value that can be either defined by string or by a secret ref. +type ValueRef struct { + StrVal *string `json:"-"` + + // deprecated + SecretRef *v1alpha1.SecretReference `json:"secretRef,omitempty"` +} + +// kubeconfigJSON is a helper struct for decoding. +type kubeconfigJSON struct { + Kubeconfig *ValueRef `json:"kubeconfig"` +} + +// valueRefJSON is a helper struct to decode json into a secret ref object. +type valueRefJSON struct { + SecretRef *v1alpha1.SecretReference `json:"secretRef,omitempty"` +} + +// MarshalJSON implements the json marshaling for a JSON +func (v ValueRef) MarshalJSON() ([]byte, error) { + if v.StrVal != nil { + return json.Marshal(v.StrVal) + } + ref := valueRefJSON{ + SecretRef: v.SecretRef, + } + return json.Marshal(ref) +} + +// UnmarshalJSON implements json unmarshaling for a JSON +func (v *ValueRef) UnmarshalJSON(data []byte) error { + ref := &valueRefJSON{} + err := json.Unmarshal(data, ref) + if err == nil && ref.SecretRef != nil { + // parsing into secret reference was successful + v.SecretRef = ref.SecretRef + return nil + } + // parse into string instead + var strVal string + err = json.Unmarshal(data, &strVal) + if err == nil { + v.StrVal = &strVal + return nil + } + v.StrVal = pointer.String(string(data)) + return nil +} + +func (kc *KubernetesClusterTargetConfig) UnmarshalJSON(data []byte) error { + kj := &kubeconfigJSON{} + err := json.Unmarshal(data, kj) + if err == nil && kj.Kubeconfig != nil { + // parsing was successful + kc.Kubeconfig = *kj.Kubeconfig + return nil + } + return kc.Kubeconfig.UnmarshalJSON(data) +} + +func (v ValueRef) OpenAPISchemaType() []string { + return []string{ + "object", + "string", + } +} +func (v ValueRef) OpenAPISchemaFormat() string { return "" } diff --git a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go index 7f939053c..b98779ad1 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go +++ b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_shared.go @@ -87,6 +87,12 @@ func NewAnyJSON(data []byte) AnyJSON { } } +// NewAnyJSONPointer returns a pointer to a new any json object. +func NewAnyJSONPointer(data []byte) *AnyJSON { + tmp := NewAnyJSON(data) + return &tmp +} + // MarshalJSON implements the json marshaling for a JSON func (s AnyJSON) MarshalJSON() ([]byte, error) { return s.RawMessage.MarshalJSON() diff --git a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go index 49652852b..4f9d0261c 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go +++ b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/types_target.go @@ -5,13 +5,9 @@ package v1alpha1 import ( - "encoding/json" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" lsschema "github.com/gardener/landscaper/apis/schema" - - "github.com/gardener/landscaper/apis/core" ) // TargetType defines the type of the target. @@ -86,12 +82,12 @@ type TargetSpec struct { // Configuration contains the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - Configuration AnyJSON `json:"config,omitempty"` + Configuration *AnyJSON `json:"config,omitempty"` // Reference to a secret containing the target type specific configuration. // Exactly one of the fields Configuration and SecretRef must be set // +optional - SecretRef *SecretReference `json:"secretRef,omitempty"` + SecretRef *LocalSecretReference `json:"secretRef,omitempty"` } // TargetTemplate exposes specific parts of a target that are used in the exports @@ -114,69 +110,13 @@ type TargetTemplate struct { Annotations map[string]string `json:"annotations,omitempty"` } -////////////////////////////// -// Target Types // -////////////////////////////// -// todo: refactor to own package - -// KubernetesClusterTargetType defines the landscaper kubernetes cluster target. -const KubernetesClusterTargetType TargetType = core.GroupName + "/kubernetes-cluster" - -// KubernetesClusterTargetConfig defines the landscaper kubernetes cluster target config. -type KubernetesClusterTargetConfig struct { - // Kubeconfig defines kubeconfig as string. - Kubeconfig ValueRef `json:"kubeconfig"` -} - -// DefaultKubeconfigKey is the default that is used to hold a kubeconfig. -const DefaultKubeconfigKey = "kubeconfig" - -// ValueRef holds a value that can be either defined by string or by a secret ref. -type ValueRef struct { - StrVal *string `json:"-"` - - // deprecated - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// valueRefJSON is a helper struct to decode json into a secret ref object. -type valueRefJSON struct { - SecretRef *SecretReference `json:"secretRef,omitempty"` -} - -// MarshalJSON implements the json marshaling for a JSON -func (v ValueRef) MarshalJSON() ([]byte, error) { - if v.StrVal != nil { - return json.Marshal(v.StrVal) - } - ref := valueRefJSON{ - SecretRef: v.SecretRef, - } - return json.Marshal(ref) -} - -// UnmarshalJSON implements json unmarshaling for a JSON -func (v *ValueRef) UnmarshalJSON(data []byte) error { - if data[0] == '"' { - var strVal string - if err := json.Unmarshal(data, &strVal); err != nil { - return err - } - v.StrVal = &strVal - return nil - } - ref := &valueRefJSON{} - if err := json.Unmarshal(data, ref); err != nil { - return err - } - v.SecretRef = ref.SecretRef - return nil -} +// ResolvedTarget is a helper struct to store a target together with the content of its resolved secret reference. +type ResolvedTarget struct { + // Target contains the original target. + *Target `json:"target"` -func (v ValueRef) OpenAPISchemaType() []string { - return []string{ - "object", - "string", - } + // Content contains the content of the target. + // If the target has a secret reference, this field should be filled by a TargetResolver. + // Otherwise, the inline configuration of the target is put here. + Content string `json:"content"` } -func (v ValueRef) OpenAPISchemaFormat() string { return "" } diff --git a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go index 1bc724bad..843e08c09 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go +++ b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.conversion.go @@ -652,6 +652,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ResolvedTarget)(nil), (*core.ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(a.(*ResolvedTarget), b.(*core.ResolvedTarget), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*core.ResolvedTarget)(nil), (*ResolvedTarget)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(a.(*core.ResolvedTarget), b.(*ResolvedTarget), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*ResourceReference)(nil), (*core.ResourceReference)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ResourceReference_To_core_ResourceReference(a.(*ResourceReference), b.(*core.ResourceReference), scope) }); err != nil { @@ -2633,6 +2643,28 @@ func Convert_core_Requirement_To_v1alpha1_Requirement(in *core.Requirement, out return autoConvert_core_Requirement_To_v1alpha1_Requirement(in, out, s) } +func autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + out.Target = (*core.Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget is an autogenerated conversion function. +func Convert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in *ResolvedTarget, out *core.ResolvedTarget, s conversion.Scope) error { + return autoConvert_v1alpha1_ResolvedTarget_To_core_ResolvedTarget(in, out, s) +} + +func autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + out.Target = (*Target)(unsafe.Pointer(in.Target)) + out.Content = in.Content + return nil +} + +// Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget is an autogenerated conversion function. +func Convert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in *core.ResolvedTarget, out *ResolvedTarget, s conversion.Scope) error { + return autoConvert_core_ResolvedTarget_To_v1alpha1_ResolvedTarget(in, out, s) +} + func autoConvert_v1alpha1_ResourceReference_To_core_ResourceReference(in *ResourceReference, out *core.ResourceReference, s conversion.Scope) error { out.ComponentName = in.ComponentName out.ResourceName = in.ResourceName @@ -2945,10 +2977,8 @@ func Convert_core_TargetSelector_To_v1alpha1_TargetSelector(in *core.TargetSelec func autoConvert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.TargetSpec, s conversion.Scope) error { out.Type = core.TargetType(in.Type) - if err := Convert_v1alpha1_AnyJSON_To_core_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*core.SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*core.AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*core.LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } @@ -2959,10 +2989,8 @@ func Convert_v1alpha1_TargetSpec_To_core_TargetSpec(in *TargetSpec, out *core.Ta func autoConvert_core_TargetSpec_To_v1alpha1_TargetSpec(in *core.TargetSpec, out *TargetSpec, s conversion.Scope) error { out.Type = TargetType(in.Type) - if err := Convert_core_AnyJSON_To_v1alpha1_AnyJSON(&in.Configuration, &out.Configuration, s); err != nil { - return err - } - out.SecretRef = (*SecretReference)(unsafe.Pointer(in.SecretRef)) + out.Configuration = (*AnyJSON)(unsafe.Pointer(in.Configuration)) + out.SecretRef = (*LocalSecretReference)(unsafe.Pointer(in.SecretRef)) return nil } diff --git a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go index 18f570677..fdb8c0b5a 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/gardener/landscaper/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -1724,23 +1724,6 @@ func (in *JSONSchemaDefinition) DeepCopy() *JSONSchemaDefinition { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KubernetesClusterTargetConfig) DeepCopyInto(out *KubernetesClusterTargetConfig) { - *out = *in - in.Kubeconfig.DeepCopyInto(&out.Kubeconfig) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesClusterTargetConfig. -func (in *KubernetesClusterTargetConfig) DeepCopy() *KubernetesClusterTargetConfig { - if in == nil { - return nil - } - out := new(KubernetesClusterTargetConfig) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LocalSecretReference) DeepCopyInto(out *LocalSecretReference) { *out = *in @@ -1887,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2216,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return @@ -2428,32 +2436,6 @@ func (in *TypedObjectReference) DeepCopy() *TypedObjectReference { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ValueRef) DeepCopyInto(out *ValueRef) { - *out = *in - if in.StrVal != nil { - in, out := &in.StrVal, &out.StrVal - *out = new(string) - **out = **in - } - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValueRef. -func (in *ValueRef) DeepCopy() *ValueRef { - if in == nil { - return nil - } - out := new(ValueRef) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VersionedNamedObjectReference) DeepCopyInto(out *VersionedNamedObjectReference) { *out = *in diff --git a/vendor/github.com/gardener/landscaper/apis/core/validation/target.go b/vendor/github.com/gardener/landscaper/apis/core/validation/target.go new file mode 100644 index 000000000..8c866e3ed --- /dev/null +++ b/vendor/github.com/gardener/landscaper/apis/core/validation/target.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Gardener contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package validation + +import ( + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/gardener/landscaper/apis/core" +) + +// ValidateTarget validates a Target +func ValidateTarget(target *core.Target) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, ValidateTargetSpec(&target.Spec, field.NewPath("spec"))...) + return allErrs +} + +func ValidateTargetSpec(spec *core.TargetSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if spec.Configuration != nil && spec.SecretRef != nil { + allErrs = append(allErrs, field.Invalid(fldPath, spec, "either config or secretRef may be set, not both")) + } + + return allErrs +} diff --git a/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go b/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go index d672d9267..0e3540f13 100644 --- a/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go +++ b/vendor/github.com/gardener/landscaper/apis/core/zz_generated.deepcopy.go @@ -1870,6 +1870,27 @@ func (in *Requirement) DeepCopy() *Requirement { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedTarget) DeepCopyInto(out *ResolvedTarget) { + *out = *in + if in.Target != nil { + in, out := &in.Target, &out.Target + *out = new(Target) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedTarget. +func (in *ResolvedTarget) DeepCopy() *ResolvedTarget { + if in == nil { + return nil + } + out := new(ResolvedTarget) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -2199,10 +2220,14 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { *out = *in - in.Configuration.DeepCopyInto(&out.Configuration) + if in.Configuration != nil { + in, out := &in.Configuration, &out.Configuration + *out = new(AnyJSON) + (*in).DeepCopyInto(*out) + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef - *out = new(SecretReference) + *out = new(LocalSecretReference) **out = **in } return diff --git a/vendor/github.com/gardener/landscaper/apis/deployer/container/constants.go b/vendor/github.com/gardener/landscaper/apis/deployer/container/constants.go index c3fd42af5..abdf8f3cb 100644 --- a/vendor/github.com/gardener/landscaper/apis/deployer/container/constants.go +++ b/vendor/github.com/gardener/landscaper/apis/deployer/container/constants.go @@ -80,6 +80,18 @@ const ComponentDescriptorPathName = "COMPONENT_DESCRIPTOR_PATH" // ComponentDescriptorPath is the path to the component descriptor file. var ComponentDescriptorPath = filepath.Join(SharedBasePath, "component_descriptor.json") +// TargetPathName is the name of the env var that points to the target content. +const TargetPathName = "TARGET_PATH" + +// TargetFileName is the name of the file that contains the target content. +const TargetFileName = "target.json" + +// TargetPath is the path to the target content file. +var TargetPath = filepath.Join(SharedBasePath, "targets", TargetFileName) + +// TargetInitDir is the directory in which the target is mounted in the init container, which then copies it to TargetPath. +var TargetInitDir = filepath.Join(BasePath, "targets") + // ContentPathName is the name of the env var that points to the blob content of the definition. const ContentPathName = "CONTENT_PATH" @@ -150,6 +162,10 @@ var ( Name: ComponentDescriptorPathName, Value: ComponentDescriptorPath, }, + { + Name: TargetPathName, + Value: TargetPath, + }, { Name: ContentPathName, Value: ContentPath, diff --git a/vendor/modules.txt b/vendor/modules.txt index 2ed982e78..1f816166b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -176,6 +176,7 @@ github.com/gardener/landscaper/apis/core github.com/gardener/landscaper/apis/core/install github.com/gardener/landscaper/apis/core/v1alpha1 github.com/gardener/landscaper/apis/core/v1alpha1/helper +github.com/gardener/landscaper/apis/core/v1alpha1/targettypes github.com/gardener/landscaper/apis/core/validation github.com/gardener/landscaper/apis/deployer/container github.com/gardener/landscaper/apis/deployer/container/install