diff --git a/hack/generators/kic/kustomize.go b/hack/generators/kic/kustomize.go new file mode 100644 index 000000000..d0d9cdbc9 --- /dev/null +++ b/hack/generators/kic/kustomize.go @@ -0,0 +1,62 @@ +package kic + +import ( + "context" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" +) + +// BuildKustomizeForURLAndRef runs kustomize build for the provided URL and ref. +// It returns the output of the kustomize build command. +func BuildKustomizeForURLAndRef(ctx context.Context, url, ref string) ([]byte, error) { + const kustomizationFileName = "kustomization.yaml" + tmpDir, err := os.MkdirTemp("", fmt.Sprintf("kustomize-kic-%s", ref)) + if err != nil { + return nil, err + } + defer os.RemoveAll(tmpDir) + + kustomizeResourceURL := fmt.Sprintf("%s?ref=%s", url, ref) + kustomization := `` + + `apiVersion: kustomize.config.k8s.io/v1beta1` + "\n" + + `kind: Kustomization` + "\n" + + `resources:` + "\n" + + `- ` + kustomizeResourceURL + "\n" + + path := filepath.Join(tmpDir, kustomizationFileName) + if err := os.WriteFile(path, []byte(kustomization), 0o644); err != nil { + return nil, err + } + + log.Printf("Running 'kustomize build' for %s\n", tmpDir) + cmd := exec.CommandContext(ctx, "kustomize", "build", tmpDir) + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + stderr, err := cmd.StderrPipe() + if err != nil { + return nil, err + } + + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("failed to start kustomize command %v: %w", cmd, err) + } + b, err := io.ReadAll(stdout) + if err != nil { + return nil, fmt.Errorf("failed to read kustomize stdout: %w", err) + } + berr, err := io.ReadAll(stderr) + if err != nil { + return nil, fmt.Errorf("failed to read kustomize stderr: %w", err) + } + if err := cmd.Wait(); err != nil { + return nil, fmt.Errorf("failed to wait for kustomize to finish, output %s: %w", string(berr), err) + } + + return b, nil +} diff --git a/hack/generators/kic/webhook-config-generator/main.go b/hack/generators/kic/webhook-config-generator/main.go index 22962d14b..8f0c12e6f 100644 --- a/hack/generators/kic/webhook-config-generator/main.go +++ b/hack/generators/kic/webhook-config-generator/main.go @@ -2,11 +2,13 @@ package main import ( "bytes" + "context" "fmt" "log" "os" "text/template" + "github.com/kong/semver/v4" "github.com/samber/lo" admregv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/util/yaml" @@ -18,25 +20,41 @@ import ( const ( validatingWebhookConfigurationPath = "config/webhook/manifests.yaml" + validatingWebhookConfigurationKustomizeURL = "https://github.com/kong/kubernetes-ingress-controller/config/webhook" validatingWebhookConfigurationGeneratorForVersionOutputPath = "pkg/utils/kubernetes/resources/validatingwebhookconfig/zz_generated_kic_%s.go" validatingWebhookConfigurationGeneratorMasterOutputPath = "pkg/utils/kubernetes/resources/zz_generated_kic_validatingwebhookconfig.go" ) func main() { - generateHelpersForAllConfiguredVersions() + generateHelpersForAllConfiguredVersions(context.Background()) generateMasterHelper() } // generateHelpersForAllConfiguredVersions iterates over kicversions.ManifestsVersionsForKICVersions map and generates // GenerateValidatingWebhookConfigurationForKIC_{versionConstraint} function for each configured version. -func generateHelpersForAllConfiguredVersions() { +func generateHelpersForAllConfiguredVersions(ctx context.Context) { for versionConstraint, version := range kicversions.ManifestsVersionsForKICVersions { log.Printf("Generating ValidatingWebhook Configuration for KIC versions %s (using manifests: %s)\n", versionConstraint, version) - // Download KIC-generated ValidatingWebhookConfiguration. - manifestContent, err := kic.GetFileFromKICRepositoryForVersion(validatingWebhookConfigurationPath, version) - if err != nil { - log.Fatalf("Failed to download %s from KIC repository: %s", validatingWebhookConfigurationPath, err) + var ( + manifestContent []byte + err error + ) + // Before KIC 3.2 config/webhook directory contained only the generated manifes YAML. + // 3.2 and later versions contain a kustomization.yaml file that use the patches from config/webhook + // directory to generate the ValidatingWebhookConfiguration. + if version.LT(semver.MustParse("3.2.0")) { + // Download KIC-generated ValidatingWebhookConfiguration. + manifestContent, err = kic.GetFileFromKICRepositoryForVersion(validatingWebhookConfigurationPath, version) + if err != nil { + log.Fatalf("Failed to download %s from KIC repository: %s", validatingWebhookConfigurationPath, err) + } + } else { + // Generate ValidatingWebhookConfiguration using KIC's webhook kustomize dir. + manifestContent, err = kic.BuildKustomizeForURLAndRef(ctx, validatingWebhookConfigurationKustomizeURL, "v"+version.String()) + if err != nil { + log.Fatalf("Failed to generate KIC's ValidatingWebhookConfiguration based on %s: %s", validatingWebhookConfigurationKustomizeURL, err) + } } // Get rid of the YAML objects separator as we know there's only one ValidatingWebhookConfiguration in the file. diff --git a/pkg/utils/kubernetes/resources/validatingwebhookconfig/zz_generated_kic_ge3_2.go b/pkg/utils/kubernetes/resources/validatingwebhookconfig/zz_generated_kic_ge3_2.go index 94d346cf3..f588a0567 100644 --- a/pkg/utils/kubernetes/resources/validatingwebhookconfig/zz_generated_kic_ge3_2.go +++ b/pkg/utils/kubernetes/resources/validatingwebhookconfig/zz_generated_kic_ge3_2.go @@ -15,6 +15,70 @@ func GenerateValidatingWebhookConfigurationForKIC_ge3_2(name string, clientConfi Name: name, }, Webhooks: []admregv1.ValidatingWebhook{ + { + Name: "secrets.credentials.validation.ingress-controller.konghq.com", + ClientConfig: clientConfig, + // We're using 'Ignore' failure policy to avoid issues with modifying resources when webhook-backing + // Deployments (ControlPlane and DataPlane) are not available. + // See https://github.com/Kong/gateway-operator/issues/1564 for more details. + FailurePolicy: lo.ToPtr(admregv1.Ignore), + MatchPolicy: lo.ToPtr(admregv1.MatchPolicyType("Equivalent")), + SideEffects: lo.ToPtr(admregv1.SideEffectClass("None")), + AdmissionReviewVersions: []string{ + "v1", + }, + Rules: []admregv1.RuleWithOperations{ + { + Rule: admregv1.Rule{ + APIGroups: []string{ + "", + }, + APIVersions: []string{ + "v1", + }, + Resources: []string{ + "secrets", + }, + }, + Operations: []admregv1.OperationType{ + "CREATE", + "UPDATE", + }, + }, + }, + }, + { + Name: "secrets.plugins.validation.ingress-controller.konghq.com", + ClientConfig: clientConfig, + // We're using 'Ignore' failure policy to avoid issues with modifying resources when webhook-backing + // Deployments (ControlPlane and DataPlane) are not available. + // See https://github.com/Kong/gateway-operator/issues/1564 for more details. + FailurePolicy: lo.ToPtr(admregv1.Ignore), + MatchPolicy: lo.ToPtr(admregv1.MatchPolicyType("Equivalent")), + SideEffects: lo.ToPtr(admregv1.SideEffectClass("None")), + AdmissionReviewVersions: []string{ + "v1", + }, + Rules: []admregv1.RuleWithOperations{ + { + Rule: admregv1.Rule{ + APIGroups: []string{ + "", + }, + APIVersions: []string{ + "v1", + }, + Resources: []string{ + "secrets", + }, + }, + Operations: []admregv1.OperationType{ + "CREATE", + "UPDATE", + }, + }, + }, + }, { Name: "httproutes.validation.ingress-controller.konghq.com", ClientConfig: clientConfig,