diff --git a/hack/validator.tmpl b/hack/validator.tmpl index c90b9149..2a0ae09f 100644 --- a/hack/validator.tmpl +++ b/hack/validator.tmpl @@ -11,7 +11,7 @@ helmReleaseSecret: exists: false imageRegistry: quay.io/validator-labs useFixedVersion: false -airgapConfig: +registryConfig: enabled: false kindConfig: useKindCluster: true diff --git a/pkg/components/environment.go b/pkg/components/environment.go index f3bd2d54..8c110719 100644 --- a/pkg/components/environment.go +++ b/pkg/components/environment.go @@ -23,32 +23,53 @@ type CACert struct { Path string `yaml:"path"` } -// Hauler represents the hauler configuration for air-gapped installs. -type Hauler struct { +// Registry represents the generic configuration for a registry. +// If IsAirgapped is true, a local Hauler registry is used. +type Registry struct { Host string `yaml:"host"` Port int `yaml:"port"` BasicAuth *BasicAuth `yaml:"basicAuth,omitempty"` InsecureSkipTLSVerify bool `yaml:"insecureSkipTLSVerify"` CACert *CACert `yaml:"caCert,omitempty"` ReuseProxyCACert bool `yaml:"reuseProxyCACert,omitempty"` + BaseContentPath string `yaml:"baseContentPath"` + IsAirgapped bool `yaml:"isAirgapped"` } -// Endpoint returns the base hauler registry URL. -func (h *Hauler) Endpoint() string { - return fmt.Sprintf("%s:%d", h.Host, h.Port) +// UnspecifiedPort is the value given to a Registry.Port when it is not specified. +const UnspecifiedPort = -1 + +// Endpoint returns the base registry URL. +func (r *Registry) Endpoint() string { + if r.Port != UnspecifiedPort { + return fmt.Sprintf("%s:%d", r.Host, r.Port) + } + return r.Host } -// KindImage returns the image with the local hauler registry endpoint. -func (h *Hauler) KindImage(image string) string { - return fmt.Sprintf("localhost:%d/%s", h.Port, image) +// KindImage returns the image with the registry endpoint. +func (r *Registry) KindImage(image string) string { + if r.IsAirgapped { + return fmt.Sprintf("localhost:%d/%s", r.Port, image) + } + if r.BaseContentPath == "" { + return fmt.Sprintf("%s/%s", r.Endpoint(), image) + } + return fmt.Sprintf("%s/%s/%s", r.Endpoint(), r.BaseContentPath, image) } -// ChartEndpoint returns the hauler chart repository URL. -func (h *Hauler) ChartEndpoint() string { - return fmt.Sprintf("oci://%s/hauler", h.Endpoint()) +// ChartEndpoint returns the chart repository URL. +func (r *Registry) ChartEndpoint() string { + if r.IsAirgapped { + return fmt.Sprintf("oci://%s/hauler", r.Endpoint()) + } + if r.BaseContentPath == "" { + return fmt.Sprintf("oci://%s/charts", r.Endpoint()) + } + return fmt.Sprintf("oci://%s/%s/charts", r.Endpoint(), r.BaseContentPath) } -// ImageEndpoint returns the hauler image repository URL. -func (h *Hauler) ImageEndpoint() string { - return fmt.Sprintf("%s/%s", h.Endpoint(), cfg.ValidatorImageRepository) +// ImageEndpoint returns the image repository URL. +func (r *Registry) ImageEndpoint() string { + return fmt.Sprintf("%s/%s", r.Endpoint(), cfg.ValidatorImageRepository) } diff --git a/pkg/components/validator.go b/pkg/components/validator.go index c3288c54..fbfafbd7 100644 --- a/pkg/components/validator.go +++ b/pkg/components/validator.go @@ -27,7 +27,7 @@ type ValidatorConfig struct { ReleaseSecret *Secret `yaml:"helmReleaseSecret"` KindConfig KindConfig `yaml:"kindConfig"` Kubeconfig string `yaml:"kubeconfig"` - AirgapConfig *AirgapConfig `yaml:"airgapConfig"` + RegistryConfig *RegistryConfig `yaml:"registryConfig"` SinkConfig *SinkConfig `yaml:"sinkConfig"` ProxyConfig *ProxyConfig `yaml:"proxyConfig"` ImageRegistry string `yaml:"imageRegistry"` @@ -52,8 +52,8 @@ func NewValidatorConfig() *ValidatorConfig { KindConfig: KindConfig{ UseKindCluster: false, }, - AirgapConfig: &AirgapConfig{ - Hauler: &Hauler{ + RegistryConfig: &RegistryConfig{ + Registry: &Registry{ BasicAuth: &BasicAuth{}, CACert: &CACert{}, }, @@ -199,10 +199,10 @@ func (c *ValidatorConfig) encrypt() error { return nil } -// AirgapConfig represents the air-gapped configuration. -type AirgapConfig struct { - Enabled bool `yaml:"enabled"` - Hauler *Hauler `yaml:"hauler"` +// RegistryConfig represents the artifact registry configuration. +type RegistryConfig struct { + Enabled bool `yaml:"enabled"` + Registry *Registry `yaml:"registry"` } // KindConfig represents the kind configuration. diff --git a/pkg/config/versions.go b/pkg/config/versions.go index 9d372496..ab1e662d 100644 --- a/pkg/config/versions.go +++ b/pkg/config/versions.go @@ -2,10 +2,10 @@ package config // ValidatorChartVersions is a map of validator component names to their respective versions var ValidatorChartVersions = map[string]string{ - Validator: "v0.0.47", + Validator: "v0.0.48", ValidatorPluginAws: "v0.1.1", ValidatorPluginAzure: "v0.0.13", - ValidatorPluginNetwork: "v0.0.18", + ValidatorPluginNetwork: "v0.0.19", ValidatorPluginOci: "v0.0.11", ValidatorPluginVsphere: "v0.0.27", } diff --git a/pkg/services/env_service.go b/pkg/services/env_service.go index 1c1a9b92..e22bae00 100644 --- a/pkg/services/env_service.go +++ b/pkg/services/env_service.go @@ -3,13 +3,19 @@ package services import ( "fmt" + "net/url" + "os" + "os/exec" + "strconv" "time" + "github.com/pkg/errors" "github.com/spectrocloud-labs/prompts-tui/prompts" "github.com/validator-labs/validatorctl/pkg/components" cfg "github.com/validator-labs/validatorctl/pkg/config" log "github.com/validator-labs/validatorctl/pkg/logging" + exec_utils "github.com/validator-labs/validatorctl/pkg/utils/exec" "github.com/validator-labs/validatorctl/pkg/utils/network" ) @@ -55,7 +61,7 @@ func ReadProxyProps(e *components.Env) error { } // ReadHaulerProps prompts the user to configure hauler settings. -func ReadHaulerProps(h *components.Hauler, e *components.Env) error { +func ReadHaulerProps(h *components.Registry, e *components.Env) error { var err error // registry @@ -74,26 +80,11 @@ func ReadHaulerProps(h *components.Hauler, e *components.Env) error { return err } - // basic auth - if h.BasicAuth == nil { - h.BasicAuth = &components.BasicAuth{} - } - h.BasicAuth.Username, h.BasicAuth.Password, err = prompts.ReadBasicCreds( - "Username", "Password", h.BasicAuth.Username, h.BasicAuth.Password, true, false, - ) + err = readAuthTLSProps(h) if err != nil { return err } - // tls verification - h.InsecureSkipTLSVerify, err = prompts.ReadBool("Allow Insecure Connection (Bypass x509 Verification)", true) - if err != nil { - return err - } - if h.InsecureSkipTLSVerify { - return nil - } - // ca cert if e.ProxyCACert.Path != "" { h.ReuseProxyCACert, err = prompts.ReadBool("Reuse proxy CA cert for Hauler registry", true) @@ -123,3 +114,163 @@ func ReadHaulerProps(h *components.Hauler, e *components.Env) error { return nil } + +// ReadRegistryProps prompts the user to configure custom private registry settings. +func ReadRegistryProps(r *components.Registry, e *components.Env) error { + ociURL, err := prompts.ReadURL( + "Registry Endpoint", "", "Invalid Registry Endpoint. A scheme is required, e.g.: 'https://'.", false, + ) + if err != nil { + return err + } + + parsedURL, err := url.Parse(ociURL) + if err != nil { + return err + } + r.Host = parsedURL.Hostname() + port := parsedURL.Port() + if port == "" { + r.Port = components.UnspecifiedPort + } else { + r.Port, err = strconv.Atoi(port) + if err != nil { + return err + } + } + + baseContentPath, err := prompts.ReadText("Registry Base Content Path", "", true, -1) + if err != nil { + return err + } + r.BaseContentPath = baseContentPath + + err = readAuthTLSProps(r) + if err != nil { + return err + } + + // ca cert + if e.ProxyCACert.Path != "" { + r.ReuseProxyCACert, err = prompts.ReadBool("Reuse proxy CA cert for OCI registry", true) + if err != nil { + return err + } + } + if r.CACert == nil { + r.CACert = &components.CACert{} + } + if r.ReuseProxyCACert { + r.CACert = e.ProxyCACert + } else { + caCertPath, caCertName, caCertData, err := prompts.ReadCACert("OCI registry CA certificate filepath", r.CACert.Path, "") + if err != nil { + return err + } + + if caCertPath != "" { + r.CACert.Data = string(caCertData) + r.CACert.Name = caCertName + r.CACert.Path = caCertPath + } + } + + return ensureDockerOciCaConfig(r.CACert, r.Host) +} + +func readAuthTLSProps(r *components.Registry) error { + var err error + + // basic auth + if r.BasicAuth == nil { + r.BasicAuth = &components.BasicAuth{} + } + r.BasicAuth.Username, r.BasicAuth.Password, err = prompts.ReadBasicCreds( + "Username", "Password", r.BasicAuth.Username, r.BasicAuth.Password, true, false, + ) + if err != nil { + return err + } + + // tls verification + r.InsecureSkipTLSVerify, err = prompts.ReadBool("Allow Insecure Connection (Bypass x509 Verification)", true) + if err != nil { + return err + } + if r.InsecureSkipTLSVerify { + return nil + } + + return nil +} + +func ensureDockerOciCaConfig(caCert *components.CACert, endpoint string) error { + // TODO: mock this function properly + if os.Getenv("IS_TEST") == "true" { + return nil + } + + dockerOciCaDir := fmt.Sprintf("/etc/docker/certs.d/%s", endpoint) + dockerOciCaPath := fmt.Sprintf("%s/%s", dockerOciCaDir, caCert.Name) + + if _, err := os.Stat(dockerOciCaPath); err != nil { + log.InfoCLI("OCI CA configuration for Docker not found") + + if err := ensureDockerCACertDir(dockerOciCaDir); err != nil { + return err + } + + cmd := exec.Command("sudo", "cp", caCert.Path, dockerOciCaPath) //#nosec G204 + _, stderr, err := exec_utils.Execute(true, cmd) + if err != nil { + log.InfoCLI("Failed to configure OCI CA certificate") + return errors.Wrap(err, stderr) + } + log.InfoCLI("Copied OCA CA certificate from %s to %s", caCert.Path, dockerOciCaPath) + + log.InfoCLI("Restarting Docker...") + cmd = exec.Command("sudo", "systemctl", "daemon-reload") + _, stderr, err = exec_utils.Execute(true, cmd) + if err != nil { + log.InfoCLI("Failed to reload systemd manager configuration") + log.InfoCLI("Please execute 'sudo systemctl daemon-reload' manually and retry") + return errors.Wrap(err, stderr) + } + + cmd = exec.Command("sudo", "systemctl", "restart", "docker") + _, stderr, err = exec_utils.Execute(true, cmd) + if err != nil { + log.InfoCLI("Failed to restart Docker") + log.InfoCLI("Please execute 'sudo systemctl restart docker' manually and retry") + return errors.Wrap(err, stderr) + } + log.InfoCLI("Configured OCA CA certificate for Docker") + } + return nil +} + +func ensureDockerCACertDir(path string) error { + fi, err := os.Stat(path) + if err != nil { + return createDockerCACertDir(path) + } + if !fi.IsDir() { + cmd := exec.Command("sudo", "rm", "-f", path) //#nosec G204 + _, stderr, err := exec_utils.Execute(true, cmd) + if err != nil { + return errors.Wrapf(err, stderr) + } + return createDockerCACertDir(path) + } + return nil +} + +func createDockerCACertDir(path string) error { + cmd := exec.Command("sudo", "mkdir", "-p", path) //#nosec G204 + _, stderr, err := exec_utils.Execute(true, cmd) + if err != nil { + return errors.Wrapf(err, stderr) + } + log.InfoCLI("Created Docker OCI CA certificate directory: %s", path) + return nil +} diff --git a/pkg/services/validator/aws_test.go b/pkg/services/validator/aws_test.go index 78076b6a..aa6cb1e1 100644 --- a/pkg/services/validator/aws_test.go +++ b/pkg/services/validator/aws_test.go @@ -16,7 +16,7 @@ import ( ) var awsDummyConfig = &components.ValidatorConfig{ - AirgapConfig: &components.AirgapConfig{ + RegistryConfig: &components.RegistryConfig{ Enabled: false, }, AWSPlugin: &components.AWSPluginConfig{ diff --git a/pkg/services/validator/common.go b/pkg/services/validator/common.go index 8b3ea703..aebed80d 100644 --- a/pkg/services/validator/common.go +++ b/pkg/services/validator/common.go @@ -38,9 +38,9 @@ func readHelmRelease(name string, k8sClient kubernetes.Interface, vc *components r.Chart.Name = name rs.Name = fmt.Sprintf("validator-helm-release-%s", name) - if vc.AirgapConfig.Enabled { - r.Chart.Repository = vc.AirgapConfig.Hauler.ChartEndpoint() - log.InfoCLI("Using local Hauler repository: %s", vc.AirgapConfig.Hauler.ChartEndpoint()) + if vc.RegistryConfig.Enabled { + r.Chart.Repository = vc.RegistryConfig.Registry.ChartEndpoint() + log.InfoCLI("Using helm repository: %s", vc.RegistryConfig.Registry.ChartEndpoint()) } else { r.Chart.Repository, err = prompts.ReadText(fmt.Sprintf("%s Helm repository", name), defaultRepo, false, -1) if err != nil { @@ -72,11 +72,7 @@ func readHelmRelease(name string, k8sClient kubernetes.Interface, vc *components } } - if err := readHelmCredentials(r, rs, k8sClient, vc); err != nil { - return err - } - - return nil + return readHelmCredentials(r, rs, k8sClient, vc) } func readHelmCredentials(r *vapi.HelmRelease, rs *components.Secret, k8sClient kubernetes.Interface, vc *components.ValidatorConfig) error { diff --git a/pkg/services/validator/network_test.go b/pkg/services/validator/network_test.go index f325c52e..1fb5109c 100644 --- a/pkg/services/validator/network_test.go +++ b/pkg/services/validator/network_test.go @@ -16,7 +16,7 @@ import ( ) var networkDummyConfig = &components.ValidatorConfig{ - AirgapConfig: &components.AirgapConfig{ + RegistryConfig: &components.RegistryConfig{ Enabled: false, }, NetworkPlugin: &components.NetworkPluginConfig{ diff --git a/pkg/services/validator/validator_service.go b/pkg/services/validator/validator_service.go index 345bca3e..9b2b6fb7 100644 --- a/pkg/services/validator/validator_service.go +++ b/pkg/services/validator/validator_service.go @@ -30,8 +30,6 @@ var ( "vSphere": readVspherePlugin, } plugins = make([]string, 0, len(pluginFuncs)) - - imageRegistry = cfg.ValidatorImagePath() ) func init() { @@ -66,24 +64,13 @@ func ReadValidatorConfig(c *cfg.Config, tc *cfg.TaskConfig, vc *components.Valid } } - log.Header("Air-gapped Configuration") - if err := readAirgapConfig(vc); err != nil { + log.Header("Proxy Configuration") + if err := readProxyConfig(vc); err != nil { return err } - if vc.AirgapConfig.Enabled { - vc.ImageRegistry = vc.AirgapConfig.Hauler.ImageEndpoint() - } else { - if vc.ImageRegistry != "" { - imageRegistry = vc.ImageRegistry - } - vc.ImageRegistry, err = prompts.ReadText("Validator image registry", imageRegistry, false, -1) - if err != nil { - return err - } - } - log.Header("Proxy Configuration") - if err := readProxyConfig(vc); err != nil { + log.Header("Artifact Registry Configuration") + if err := readRegistryConfig(vc); err != nil { return err } @@ -190,8 +177,8 @@ func UpdateValidatorCredentials(c *components.ValidatorConfig) error { } } - if c.AirgapConfig.Enabled { - if err := readAirgapConfig(c); err != nil { + if c.RegistryConfig.Enabled { + if err := readRegistryConfig(c); err != nil { return err } } @@ -238,13 +225,46 @@ func UpdateValidatorCredentials(c *components.ValidatorConfig) error { return nil } -func readAirgapConfig(vc *components.ValidatorConfig) (err error) { - vc.AirgapConfig.Enabled, err = prompts.ReadBool("Configure Hauler for air-gapped installation", false) - if err != nil || !vc.AirgapConfig.Enabled { - return +func readRegistryConfig(vc *components.ValidatorConfig) (err error) { + airgapped, err := prompts.ReadBool("Configure Hauler for air-gapped installation", false) + if err != nil { + return err + } + if airgapped { + vc.RegistryConfig.Enabled = true + vc.RegistryConfig.Registry.IsAirgapped = true + vc.UseFixedVersions = true + if err = services.ReadHaulerProps(vc.RegistryConfig.Registry, vc.ProxyConfig.Env); err != nil { + return err + } + vc.ImageRegistry = vc.RegistryConfig.Registry.ImageEndpoint() + return nil + } + + privateRegistry, err := prompts.ReadBool("Configure private OCI registry", false) + if err != nil { + return err + } + if privateRegistry { + vc.RegistryConfig.Enabled = true + if err := services.ReadRegistryProps(vc.RegistryConfig.Registry, vc.ProxyConfig.Env); err != nil { + return err + } + vc.ImageRegistry = vc.RegistryConfig.Registry.ImageEndpoint() + return nil + } + + // public registry configuration + imageRegistry := cfg.ValidatorImagePath() + if vc.ImageRegistry != "" { + imageRegistry = vc.ImageRegistry + } + vc.ImageRegistry, err = prompts.ReadText("Validator image registry", imageRegistry, false, -1) + if err != nil { + return err } - vc.UseFixedVersions = true - return services.ReadHaulerProps(vc.AirgapConfig.Hauler, vc.ProxyConfig.Env) + return nil + } func readProxyConfig(vc *components.ValidatorConfig) error { diff --git a/pkg/services/validator/vmware_test.go b/pkg/services/validator/vmware_test.go index 4bc164e1..b15ace1f 100644 --- a/pkg/services/validator/vmware_test.go +++ b/pkg/services/validator/vmware_test.go @@ -18,7 +18,7 @@ import ( ) var vSphereDummyConfig = &components.ValidatorConfig{ - AirgapConfig: &components.AirgapConfig{ + RegistryConfig: &components.RegistryConfig{ Enabled: false, }, VspherePlugin: &components.VspherePluginConfig{ diff --git a/pkg/utils/embed/embed.go b/pkg/utils/embed/embed.go index 8f6b0065..b08a3390 100644 --- a/pkg/utils/embed/embed.go +++ b/pkg/utils/embed/embed.go @@ -38,10 +38,8 @@ func RenderTemplate(args interface{}, dir, filename, outputPath string) error { if err != nil { return err } - if err := WriteFile(outputPath, data); err != nil { - return err - } - return nil + + return WriteFile(outputPath, data) } // RenderTemplateBytes renders a template from the embedded file system and returns the resulting bytes. diff --git a/pkg/utils/kind/kind.go b/pkg/utils/kind/kind.go index 34cdf147..b16df0da 100644 --- a/pkg/utils/kind/kind.go +++ b/pkg/utils/kind/kind.go @@ -90,21 +90,26 @@ func RenderKindConfig(vc *components.ValidatorConfig, kindConfig string) error { "Image": image, } - if vc.AirgapConfig != nil && vc.AirgapConfig.Enabled { - hauler := vc.AirgapConfig.Hauler - ep := hauler.Endpoint() - clusterConfigArgs["Image"] = hauler.KindImage(image) + r := getRegistry(vc) + + // registry configuration + if r != nil { + ep := r.Endpoint() + clusterConfigArgs["Image"] = r.KindImage(image) clusterConfigArgs["RegistryEndpoint"] = ep - clusterConfigArgs["RegistryInsecure"] = strconv.FormatBool(hauler.InsecureSkipTLSVerify) - clusterConfigArgs["RegistryMirrors"] = defaultMirrorRegistries(ep) - clusterConfigArgs["ReusedProxyCACert"] = hauler.ReuseProxyCACert + clusterConfigArgs["RegistryInsecure"] = strconv.FormatBool(r.InsecureSkipTLSVerify) + clusterConfigArgs["RegistryMirrors"] = defaultMirrorRegistries(ep, r.BaseContentPath) + clusterConfigArgs["ReusedProxyCACert"] = r.ReuseProxyCACert - if hauler.CACert != nil { - clusterConfigArgs["RegistryCACertName"] = hauler.CACert.Name + if r.CACert != nil { + clusterConfigArgs["RegistryCACertName"] = r.CACert.Name + } + if r.BasicAuth != nil { + clusterConfigArgs["RegistryUsername"] = r.BasicAuth.Username + clusterConfigArgs["RegistryPassword"] = r.BasicAuth.Password } - if hauler.BasicAuth != nil { - clusterConfigArgs["RegistryUsername"] = hauler.BasicAuth.Username - clusterConfigArgs["RegistryPassword"] = hauler.BasicAuth.Password + if r.BaseContentPath != "" { + clusterConfigArgs["RegistryBaseContentPath"] = r.BaseContentPath } } @@ -112,7 +117,7 @@ func RenderKindConfig(vc *components.ValidatorConfig, kindConfig string) error { } // defaultMirrorRegistries returns a comma-separated string of default registry mirrors -func defaultMirrorRegistries(registryEndpoint string) []string { +func defaultMirrorRegistries(registryEndpoint, baseContentPath string) []string { if registryEndpoint == "" { return nil } @@ -120,6 +125,9 @@ func defaultMirrorRegistries(registryEndpoint string) []string { for _, registry := range cfg.RegistryMirrors { // Add OCI format suffix (/v2) registryMirrorEndpoint := fmt.Sprintf("%s/v2", registryEndpoint) + if baseContentPath != "" { + registryMirrorEndpoint = fmt.Sprintf("%s/%s", registryMirrorEndpoint, baseContentPath) + } mirrorRegistries = append(mirrorRegistries, fmt.Sprintf("%s%s%s", registry, cfg.RegistryMirrorSeparator, registryMirrorEndpoint), ) @@ -163,3 +171,11 @@ func updateCaCerts(name string) error { } return nil } + +func getRegistry(vc *components.ValidatorConfig) *components.Registry { + if vc.RegistryConfig.Enabled { + return vc.RegistryConfig.Registry + } + + return nil +} diff --git a/pkg/utils/kind/kind_test.go b/pkg/utils/kind/kind_test.go index 40ad8a88..58a52ea5 100644 --- a/pkg/utils/kind/kind_test.go +++ b/pkg/utils/kind/kind_test.go @@ -16,6 +16,22 @@ func TestRenderKindConfig(t *testing.T) { vc *components.ValidatorConfig expected string }{ + { + name: "Kind config basic", + vc: &components.ValidatorConfig{ + ProxyConfig: &components.ProxyConfig{ + Env: &components.Env{ + ProxyCACert: &components.CACert{}, + PodCIDR: &cfg.DefaultPodCIDR, + ServiceIPRange: &cfg.DefaultServiceIPRange, + }, + }, + RegistryConfig: &components.RegistryConfig{ + Enabled: false, + }, + }, + expected: "kindconfig-basic.yaml", + }, { name: "Kind config w/ proxy CA cert", vc: &components.ValidatorConfig{ @@ -29,11 +45,42 @@ func TestRenderKindConfig(t *testing.T) { }, }, }, + RegistryConfig: &components.RegistryConfig{ + Enabled: false, + }, }, expected: "kindconfig-shared-ca.yaml", }, { - name: "Kind config basic", + name: "Kind config basic w/ custom registry", + vc: &components.ValidatorConfig{ + ProxyConfig: &components.ProxyConfig{ + Env: &components.Env{ + ProxyCACert: &components.CACert{}, + PodCIDR: &cfg.DefaultPodCIDR, + ServiceIPRange: &cfg.DefaultServiceIPRange, + }, + }, + RegistryConfig: &components.RegistryConfig{ + Enabled: true, + Registry: &components.Registry{ + Host: "registry.example.com", + Port: components.UnspecifiedPort, + BasicAuth: &components.BasicAuth{ + Username: "user", + Password: "password", + }, + InsecureSkipTLSVerify: true, + ReuseProxyCACert: false, + BaseContentPath: "base-path", + IsAirgapped: false, + }, + }, + }, + expected: "kindconfig-custom-registry.yaml", + }, + { + name: "Kind config basic w/ airgapped registry", vc: &components.ValidatorConfig{ ProxyConfig: &components.ProxyConfig{ Env: &components.Env{ @@ -42,8 +89,22 @@ func TestRenderKindConfig(t *testing.T) { ServiceIPRange: &cfg.DefaultServiceIPRange, }, }, + RegistryConfig: &components.RegistryConfig{ + Enabled: true, + Registry: &components.Registry{ + Host: "registry.example.com", + Port: 5000, + BasicAuth: &components.BasicAuth{ + Username: "user", + Password: "password", + }, + InsecureSkipTLSVerify: true, + ReuseProxyCACert: false, + IsAirgapped: true, + }, + }, }, - expected: "kindconfig-basic.yaml", + expected: "kindconfig-airgapped.yaml", }, } for _, tt := range tests { diff --git a/tests/integration/_validator/testcases/data/validator.yaml b/tests/integration/_validator/testcases/data/validator.yaml index 03bdd790..56d39b27 100644 --- a/tests/integration/_validator/testcases/data/validator.yaml +++ b/tests/integration/_validator/testcases/data/validator.yaml @@ -2,7 +2,7 @@ helmRelease: chart: name: validator repository: https://validator-labs.github.io/validator - version: v0.0.47 + version: v0.0.48 insecureSkipVerify: true values: "" helmReleaseSecret: @@ -11,7 +11,7 @@ helmReleaseSecret: exists: false imageRegistry: quay.io/validator-labs useFixedVersion: false -airgapConfig: +registryConfig: enabled: false kindConfig: useKindCluster: true @@ -161,7 +161,7 @@ networkPlugin: chart: name: validator-plugin-network repository: https://validator-labs.github.io/validator-plugin-network - version: v0.0.18 + version: v0.0.19 insecureSkipVerify: true values: "" helmReleaseSecret: diff --git a/tests/integration/_validator/testcases/test_validator.go b/tests/integration/_validator/testcases/test_validator.go index 843d9369..b86aba7b 100644 --- a/tests/integration/_validator/testcases/test_validator.go +++ b/tests/integration/_validator/testcases/test_validator.go @@ -100,15 +100,18 @@ func (t *ValidatorTest) testDeployInteractive(ctx *test.TestContext) (tr *test.T // Kind "y", // provision & use kind cluster + // Proxy + "n", // Configure an HTTP proxy + // Air-gapped "n", // enable air-gapped mode + // Private OCI registry + "n", // enable private OCI registry + // Image registry "quay.io/validator-labs", // validator image registry - // Proxy - "n", // Configure an HTTP proxy - // Sink "y", // Configure a sink "Alertmanager", // Sink type diff --git a/tests/unit-test-data/kindconfig-airgapped.yaml b/tests/unit-test-data/kindconfig-airgapped.yaml new file mode 100644 index 00000000..7686f282 --- /dev/null +++ b/tests/unit-test-data/kindconfig-airgapped.yaml @@ -0,0 +1,30 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + podSubnet: 192.168.0.0/16 + serviceSubnet: 10.96.0.0/12 + disableDefaultCNI: false +nodes: +- role: control-plane + image: localhost:5000/kindest/node:v1.30.2 +containerdConfigPatches: + - |- + [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com:5000".auth] + password = "password" + username = "user" + [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com:5000".tls] + insecure_skip_verify = true + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"] + endpoint = ["http://registry.example.com:5000/v2"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."*"] + endpoint = ["http://registry.example.com:5000/v2"] \ No newline at end of file diff --git a/tests/unit-test-data/kindconfig-custom-registry.yaml b/tests/unit-test-data/kindconfig-custom-registry.yaml new file mode 100644 index 00000000..30f832e9 --- /dev/null +++ b/tests/unit-test-data/kindconfig-custom-registry.yaml @@ -0,0 +1,30 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +networking: + podSubnet: 192.168.0.0/16 + serviceSubnet: 10.96.0.0/12 + disableDefaultCNI: false +nodes: +- role: control-plane + image: registry.example.com/base-path/kindest/node:v1.30.2 +containerdConfigPatches: + - |- + [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com".auth] + password = "password" + username = "user" + [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.example.com".tls] + insecure_skip_verify = true + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"] + endpoint = ["http://registry.example.com/v2/base-path"] + [plugins."io.containerd.grpc.v1.cri".registry.mirrors."*"] + endpoint = ["http://registry.example.com/v2/base-path"] \ No newline at end of file