diff --git a/pkg/app/app_fetch.go b/pkg/app/app_fetch.go index ff9a827bb..fd0ca8eab 100644 --- a/pkg/app/app_fetch.go +++ b/pkg/app/app_fetch.go @@ -8,11 +8,17 @@ import ( "fmt" "path" "strconv" + "strings" "time" "github.com/vmware-tanzu/carvel-kapp-controller/pkg/exec" ) +const ( + addCACertMissingHintMsg = "(hint: The CA Certificate from URL is unknown/invalid. Add valid CA certificate to the kapp-controller configuration to reconcile successfully)" + caCertMissingError = "x509: certificate signed by unknown authority" +) + func (a *App) fetch(dstPath string) (string, exec.CmdRunResult) { if len(a.app.Spec.Fetch) == 0 { return "", exec.NewCmdRunResultWithErr(fmt.Errorf("Expected at least one fetch option")) @@ -67,6 +73,9 @@ func (a *App) fetch(dstPath string) (string, exec.CmdRunResult) { } } if result.Error != nil { + if strings.Contains(result.Stderr, caCertMissingError) { + result.Stderr = fmt.Sprintf("%s%s", result.Stderr, addCACertMissingHintMsg) + } return "", result } } @@ -76,6 +85,12 @@ func (a *App) fetch(dstPath string) (string, exec.CmdRunResult) { dstPath = path.Join(dstPath, "0") } + if result.Error != nil { + if strings.Contains(result.Stderr, caCertMissingError) { + result.Stderr = fmt.Sprintf("%s%s", result.Stderr, addCACertMissingHintMsg) + } + } + return dstPath, result } diff --git a/test/e2e/kappcontroller/config_test.go b/test/e2e/kappcontroller/config_test.go index 30c2cd26d..01199fba0 100644 --- a/test/e2e/kappcontroller/config_test.go +++ b/test/e2e/kappcontroller/config_test.go @@ -422,3 +422,88 @@ stringData: assert.Equal(t, actualSyncPeriod, expectedSyncPeriod) }) } + +func TestConfig_NoCACerts(t *testing.T) { + env := e2e.BuildEnv(t) + logger := e2e.Logger{} + kapp := e2e.Kapp{t, env.Namespace, logger} + sas := e2e.ServiceAccounts{env.Namespace} + + name := "test-https" + pkgrName := "test-https-pkgr" + httpsServerName := "test-https-server" + configName := "test-config-missing-ca-config" + + yaml1 := fmt.Sprintf(`--- +apiVersion: kappctrl.k14s.io/v1alpha1 +kind: App +metadata: + name: test-https + annotations: + kapp.k14s.io/change-group: kappctrl-e2e.k14s.io/apps +spec: + serviceAccountName: kappctrl-e2e-ns-sa + fetch: + - http: + # use https to exercise CA certificate validation + # When updating address, certs and keys must be regenerated + # for server and added to e2e/assets/https-server + url: https://https-svc.https-server.svc.cluster.local:443/deployment.yml + template: + - ytt: {} + deploy: + - kapp: + inspect: {} + intoNs: %s +`, env.Namespace) + sas.ForNamespaceYAML() + + cleanUp := func() { + kapp.Run([]string{"delete", "-a", name}) + kapp.Run([]string{"delete", "-a", pkgrName}) + kapp.Run([]string{"delete", "-a", configName}) + kapp.Run([]string{"delete", "-a", httpsServerName}) + } + cleanUp() + defer cleanUp() + + logger.Section("deploy controller config with no CA cert", func() { + config := ` +apiVersion: v1 +kind: Secret +metadata: + name: kapp-controller-config + namespace: kapp-controller +stringData: + # CA Cert is not present + caCerts: +` + kapp.RunWithOpts([]string{"deploy", "-f", "-", "-a", configName}, + e2e.RunOpts{StdinReader: strings.NewReader(config)}) + + // Since config propagation is async, just wait a little bit + time.Sleep(2 * time.Second) + }) + + logger.Section("deploy https server with self signed certs", func() { + kapp.Run([]string{"deploy", "-f", "../assets/https-server/", "-a", httpsServerName}) + }) + + logger.Section("deploy app that tries to fetch content from https server (whose certs are not uploaded to kapp-controller)", func() { + _, err := kapp.RunWithOpts([]string{"deploy", "-f", "-", "-a", name}, e2e.RunOpts{ + StdinReader: strings.NewReader(yaml1), + AllowError: true, + OnErrKubectl: []string{"get", "app/test-https", "-oyaml"}, + }) + assert.Error(t, err, "Expected fetching error") + + var cr v1alpha1.App + + out := kapp.Run([]string{"inspect", "-a", name, "--raw", "--tty=false", "--filter-kind=App"}) + assert.NoError(t, yaml.Unmarshal([]byte(out), &cr)) + + assert.NotNil(t, cr.Status.Fetch) + assert.Equal(t, cr.Status.Fetch.ExitCode, 1) + assert.Contains(t, cr.Status.Fetch.Stderr, "x509: certificate signed by unknown authority") + assert.Contains(t, cr.Status.Fetch.Stderr, "(hint: The CA Certificate from URL is unknown/invalid. Add valid CA certificate to the kapp-controller configuration to reconcile successfully)") + }) +}