package cli import ( "io" "os" "strings" "testing" portainer "github.com/portainer/portainer/api" zerolog "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" ) func TestOptionParser(t *testing.T) { p := Service{} require.NotNil(t, p) a := os.Args defer func() { os.Args = a }() os.Args = []string{"portainer", "--edge-compute"} opts, err := p.ParseFlags("2.34.5") require.NoError(t, err) require.False(t, *opts.HTTPDisabled) require.True(t, *opts.EnableEdgeComputeFeatures) } func TestParseKubectlShellImageFlag(t *testing.T) { tests := []struct { name string args []string envVars map[string]string expectedKubectlShellImageSet bool expectedKubectlShellFlag string }{ { name: "no flag, no env var", expectedKubectlShellImageSet: false, expectedKubectlShellFlag: portainer.DefaultKubectlShellImage, }, { name: "explicit flag", args: []string{"portainer", "--kubectl-shell-image=myimage:v2"}, expectedKubectlShellImageSet: true, expectedKubectlShellFlag: "myimage:v2", }, { name: "env var", envVars: map[string]string{portainer.KubectlShellImageEnvVar: "myimage:v3"}, expectedKubectlShellImageSet: true, expectedKubectlShellFlag: "myimage:v3", }, { name: "both env var and flag set", args: []string{"portainer", "--kubectl-shell-image=myimage:v2"}, envVars: map[string]string{portainer.KubectlShellImageEnvVar: "myimage:v3"}, expectedKubectlShellImageSet: true, expectedKubectlShellFlag: "myimage:v2", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if tc.args == nil { tc.args = []string{"portainer"} } setOsArgs(t, tc.args) for k, v := range tc.envVars { t.Setenv(k, v) } flags, err := Service{}.ParseFlags("test-version") require.NoError(t, err) require.Equal(t, tc.expectedKubectlShellImageSet, flags.KubectlShellImageSet) require.Equal(t, tc.expectedKubectlShellFlag, *flags.KubectlShellImage) }) } } func TestParseTLSFlags(t *testing.T) { testCases := []struct { name string args []string expectedTLSFlag bool expectedTLSCertFlag string expectedTLSKeyFlag string expectedLogMessages []string }{ { name: "no flags", expectedTLSFlag: false, expectedTLSCertFlag: "", expectedTLSKeyFlag: "", }, { name: "only ssl flag", args: []string{ "portainer", "--ssl", }, expectedTLSFlag: true, expectedTLSCertFlag: "", expectedTLSKeyFlag: "", }, { name: "only tls flag", args: []string{ "portainer", "--tlsverify", }, expectedTLSFlag: true, expectedTLSCertFlag: defaultTLSCertPath, expectedTLSKeyFlag: defaultTLSKeyPath, }, { name: "partial ssl flags", args: []string{ "portainer", "--ssl", "--sslcert=ssl-cert-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "ssl-cert-flag-value", expectedTLSKeyFlag: "", }, { name: "partial tls flags", args: []string{ "portainer", "--tlsverify", "--tlscert=tls-cert-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "tls-cert-flag-value", expectedTLSKeyFlag: defaultTLSKeyPath, }, { name: "partial tls and ssl flags", args: []string{ "portainer", "--tlsverify", "--tlscert=tls-cert-flag-value", "--sslkey=ssl-key-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "tls-cert-flag-value", expectedTLSKeyFlag: "ssl-key-flag-value", }, { name: "partial tls and ssl flags 2", args: []string{ "portainer", "--ssl", "--tlscert=tls-cert-flag-value", "--sslkey=ssl-key-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "tls-cert-flag-value", expectedTLSKeyFlag: "ssl-key-flag-value", }, { name: "ssl flags", args: []string{ "portainer", "--ssl", "--sslcert=ssl-cert-flag-value", "--sslkey=ssl-key-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "ssl-cert-flag-value", expectedTLSKeyFlag: "ssl-key-flag-value", expectedLogMessages: []string{ "the \\\"ssl\\\" flag is deprecated. use \\\"tlsverify\\\" instead.", "the \\\"sslcert\\\" flag is deprecated. use \\\"tlscert\\\" instead.", "the \\\"sslkey\\\" flag is deprecated. use \\\"tlskey\\\" instead.", }, }, { name: "tls flags", args: []string{ "portainer", "--tlsverify", "--tlscert=tls-cert-flag-value", "--tlskey=tls-key-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "tls-cert-flag-value", expectedTLSKeyFlag: "tls-key-flag-value", }, { name: "tls and ssl flags", args: []string{ "portainer", "--tlsverify", "--tlscert=tls-cert-flag-value", "--tlskey=tls-key-flag-value", "--ssl", "--sslcert=ssl-cert-flag-value", "--sslkey=ssl-key-flag-value", }, expectedTLSFlag: true, expectedTLSCertFlag: "tls-cert-flag-value", expectedTLSKeyFlag: "tls-key-flag-value", expectedLogMessages: []string{ "the \\\"ssl\\\" flag is deprecated. use \\\"tlsverify\\\" instead.", "the \\\"sslcert\\\" flag is deprecated. use \\\"tlscert\\\" instead.", "the \\\"sslkey\\\" flag is deprecated. use \\\"tlskey\\\" instead.", }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var logOutput strings.Builder setupLogOutput(t, &logOutput) if tc.args == nil { tc.args = []string{"portainer"} } setOsArgs(t, tc.args) s := Service{} flags, err := s.ParseFlags("test-version") if err != nil { t.Fatalf("error parsing flags: %v", err) } if flags.TLS == nil { t.Fatal("TLS flag was nil") } require.Equal(t, tc.expectedTLSFlag, *flags.TLS, "tlsverify flag didn't match") require.Equal(t, tc.expectedTLSCertFlag, *flags.TLSCert, "tlscert flag didn't match") require.Equal(t, tc.expectedTLSKeyFlag, *flags.TLSKey, "tlskey flag didn't match") for _, expectedLogMessage := range tc.expectedLogMessages { require.Contains(t, logOutput.String(), expectedLogMessage, "Log didn't contain expected message") } }) } } func setOsArgs(t *testing.T, args []string) { t.Helper() previousArgs := os.Args os.Args = args t.Cleanup(func() { os.Args = previousArgs }) } func setupLogOutput(t *testing.T, w io.Writer) { t.Helper() oldLogger := zerolog.Logger zerolog.Logger = zerolog.Output(w) t.Cleanup(func() { zerolog.Logger = oldLogger }) }