Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions internal/webhook/v1alpha1/plugin_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ var pluginsAllowedInCentralCluster = []string{
"alerts", "doop", "heureka", "kube-monitoring", "kubeconfig-generator", "perses", "repo-guard", "service-proxy", "teams2slack", "thanos",
}

// This is the prefix to identify secrets referenced directly from vault/openBao.
// TODO: Consume this constant from the tool integrating Greenhouse with vault/openBao, once implemented.
// TODO: Update docs once the complete flow is implemented
// https://github.com/cloudoperators/greenhouse/issues/1211
const (
VaultPrefix string = "vault+kvv2:///"
)

// SetupPluginWebhookWithManager configures the webhook for the Plugin custom resource.
func SetupPluginWebhookWithManager(mgr ctrl.Manager) error {
return webhook.SetupWebhook(mgr,
Expand Down Expand Up @@ -244,8 +252,14 @@ func validatePluginOptionValues(
if pluginOption.Type == greenhousev1alpha1.PluginOptionTypeSecret {
switch {
case val.Value != nil:
allErrs = append(allErrs, field.TypeInvalid(fieldPathWithIndex.Child("value"), "*****",
fmt.Sprintf("optionValue %s of type secret must use valueFrom to reference a secret", val.Name)))
var valStr string
if err := json.Unmarshal(val.Value.Raw, &valStr); err != nil {
allErrs = append(allErrs, field.TypeInvalid(fieldPathWithIndex.Child("value"), "*****", err.Error()))
}
if !strings.HasPrefix(valStr, VaultPrefix) {
allErrs = append(allErrs, field.TypeInvalid(fieldPathWithIndex.Child("value"), "*****",
fmt.Sprintf("optionValue %s of type secret without secret reference must use value with vault reference prefixed by schema %q", val.Name, VaultPrefix)))
}
continue
case val.ValueFrom != nil:
if val.ValueFrom.Secret.Name == "" {
Expand Down
20 changes: 10 additions & 10 deletions internal/webhook/v1alpha1/plugin_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ var _ = Describe("Validate Plugin OptionValues", func() {
Entry("PluginOption Value Consistent With PluginOption Type Map", map[string]any{"key": "value"}, greenhousev1alpha1.PluginOptionTypeMap, map[string]any{"key": "custom"}, false),
Entry("PluginOption Value Inconsistent With PluginOption Type Map", map[string]any{"key": "value"}, greenhousev1alpha1.PluginOptionTypeMap, "one", true),
Entry("PluginOption Value Consistent With PluginOption Type Map Nested Map", map[string]any{"key": map[string]any{"nestedKey": "value"}}, greenhousev1alpha1.PluginOptionTypeMap, map[string]any{"key": map[string]any{"nestedKey": "custom"}}, false),
Entry("PluginOption Value not supported With PluginOption Type Secret", "", greenhousev1alpha1.PluginOptionTypeSecret, "string", true),
Entry("PluginOption Value Consistent With PluginOption Type Secret", "", greenhousev1alpha1.PluginOptionTypeSecret, "vault+kvv2:///some-path/to/secret", false),
Entry("PluginOption Value Inconsistent With PluginOption Type Secret", "", greenhousev1alpha1.PluginOptionTypeSecret, "some-string", true),
)

DescribeTable("Validate PluginOptionValue references a Secret", func(actValue *greenhousev1alpha1.ValueFromSource, expErr bool) {
DescribeTable("Validate PluginOptionValue references a Secret", func(actValue *greenhousev1alpha1.PluginOptionValue, expErr bool) {
pluginDefinition := &greenhousev1alpha1.ClusterPluginDefinition{
ObjectMeta: metav1.ObjectMeta{
Namespace: "greenhouse",
Expand All @@ -140,10 +141,7 @@ var _ = Describe("Validate Plugin OptionValues", func() {
}

optionValues := []greenhousev1alpha1.PluginOptionValue{
{
Name: "test",
ValueFrom: actValue,
},
*actValue,
}

optionsFieldPath := field.NewPath("spec").Child("optionValues")
Expand All @@ -155,10 +153,12 @@ var _ = Describe("Validate Plugin OptionValues", func() {
Expect(errList).To(BeEmpty(), "expected no error, got %v", errList)
}
},
Entry("PluginOption ValueFrom has a valid SecretReference", &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Name: "secret", Key: "key"}}, false),
Entry("PluginOption ValueFrom is missing SecretReference Name", &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Key: "key"}}, true),
Entry("PluginOption ValueFrom is missing SecretReference Key", &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Name: "secret"}}, true),
Entry("PluginOption ValueFrom does not contain a SecretReference", nil, true),
Entry("PluginOption ValueFrom has a valid SecretReference", &greenhousev1alpha1.PluginOptionValue{Name: "test", ValueFrom: &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Name: "secret", Key: "key"}}}, false),
Entry("PluginOption Value has a valid string with vault schema prefix", &greenhousev1alpha1.PluginOptionValue{Name: "test", Value: test.MustReturnJSONFor("vault+kvv2:///some-path/to/secret")}, false),
Entry("PluginOption Value has a invalid string", &greenhousev1alpha1.PluginOptionValue{Name: "test", Value: test.MustReturnJSONFor("some-string")}, true),
Entry("PluginOption ValueFrom is missing SecretReference Name", &greenhousev1alpha1.PluginOptionValue{Name: "test", ValueFrom: &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Key: "key"}}}, true),
Entry("PluginOption ValueFrom is missing SecretReference Key", &greenhousev1alpha1.PluginOptionValue{Name: "test", ValueFrom: &greenhousev1alpha1.ValueFromSource{Secret: &greenhousev1alpha1.SecretKeyReference{Name: "secret"}}}, true),
Entry("PluginOption ValueFrom does not contain a SecretReference", &greenhousev1alpha1.PluginOptionValue{Name: "test"}, true),
)

Describe("Validate Plugin specifies all required options", func() {
Expand Down
Loading