diff --git a/cli/cmd/component.go b/cli/cmd/component.go index 27ab76a70..716cf2a5a 100644 --- a/cli/cmd/component.go +++ b/cli/cmd/component.go @@ -233,6 +233,18 @@ func runComponentsInstall(_ *cobra.Command, args []string) (err error) { return } + cli.StartProgress(fmt.Sprintf("Configuring component %s...", component.Name)) + + // component life cycle: initialize + stdout, stderr, errCmd := component.RunAndReturn([]string{"cdk-init"}, nil, cli.envs()...) + if errCmd != nil { + cli.Log.Warnw("component life cycle", + "error", errCmd.Error(), "stdout", stdout, "stderr", stderr) + } else { + cli.Log.Infow("component life cycle", "stdout", stdout, "stderr", stderr) + } + + cli.StopProgress() cli.OutputHuman("The component %s was installed.\n", args[0]) return } diff --git a/lwcomponent/component_test.go b/lwcomponent/component_test.go index 7ec31343d..0cd74d158 100644 --- a/lwcomponent/component_test.go +++ b/lwcomponent/component_test.go @@ -53,9 +53,13 @@ var ( helloWorld []byte //go:embed test_resources/hello-world2.sig helloWorldSig string + //go:embed test_resources/env-vars.sh + envVars []byte + //go:embed test_resources/env-vars.sig + envVarsSig string ) -func ensureMockComponent(version, signature string) (string, error) { +func ensureMockComponent(version, content, signature string) (string, error) { cPath, err := mockComponent.Path() if err.Error() != "component not found on disk" { return "", err @@ -79,11 +83,11 @@ func ensureMockComponent(version, signature string) (string, error) { } } - return cPath, os.WriteFile(cPath, helloWorld, 0744) + return cPath, os.WriteFile(cPath, []byte(content), 0744) } func TestGetComponent(t *testing.T) { - componentDir, err := ensureMockComponent("", "") + componentDir, err := ensureMockComponent("", "", "") if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -158,7 +162,7 @@ var currentVersionTests = []currentVersionTest{ func TestCurrentVersion(t *testing.T) { for _, cvt := range currentVersionTests { t.Run(cvt.Name, func(t *testing.T) { - componentDir, err := ensureMockComponent(cvt.Version, "") + componentDir, err := ensureMockComponent(cvt.Version, "", "") if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -208,7 +212,7 @@ var currentSignatureTests = []currentSignatureTest{ func TestCurrentSignature(t *testing.T) { for _, cst := range currentSignatureTests { t.Run(cst.Name, func(t *testing.T) { - componentDir, err := ensureMockComponent("", cst.Signature) + componentDir, err := ensureMockComponent("", string(helloWorld), cst.Signature) if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -253,7 +257,7 @@ var updateAvailableTests = []updateAvailableTest{ func TestUpdateAvailable(t *testing.T) { for _, uat := range updateAvailableTests { t.Run(uat.Name, func(t *testing.T) { - componentDir, err := ensureMockComponent(uat.Version, "") + componentDir, err := ensureMockComponent(uat.Version, "", "") if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -267,7 +271,7 @@ func TestUpdateAvailable(t *testing.T) { } func TestComponentStatus(t *testing.T) { - componentDir, err := ensureMockComponent("", "") + componentDir, err := ensureMockComponent("", "", "") if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -281,12 +285,14 @@ type RunAndReturnTest struct { Name string Component lwcomponent.Component Version string + Content string Signature string Args []string Stdin io.Reader ExpectedStdout string ExpectedStderr string Error error + Envs []string } var RunAndReturnTests = []RunAndReturnTest{ @@ -294,6 +300,7 @@ var RunAndReturnTests = []RunAndReturnTest{ Name: "OK", Component: mockComponent, Version: "1.0.0", + Content: string(helloWorld), Signature: helloWorldSig, Args: []string{"World"}, Stdin: strings.NewReader("Error"), @@ -301,18 +308,28 @@ var RunAndReturnTests = []RunAndReturnTest{ ExpectedStderr: "Hello Error!\n", Error: nil, }, + RunAndReturnTest{ + Name: "env-vars", + Component: mockComponent, + Version: "1.0.0", + Content: string(envVars), + Signature: envVarsSig, + Args: []string{"cdk-init"}, + ExpectedStdout: "Hello from the environment!\n", + Envs: []string{"LW_TEST=from the environment"}, + }, } func TestRunAndReturn(t *testing.T) { for _, rart := range RunAndReturnTests { t.Run(rart.Name, func(t *testing.T) { - componentDir, err := ensureMockComponent(rart.Version, rart.Signature) + componentDir, err := ensureMockComponent(rart.Version, rart.Content, rart.Signature) if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } defer os.RemoveAll(componentDir) - actualStdout, actualStderr, actualError := rart.Component.RunAndReturn(rart.Args, rart.Stdin) + actualStdout, actualStderr, actualError := rart.Component.RunAndReturn(rart.Args, rart.Stdin, rart.Envs...) if rart.Error != nil { assert.Equal(t, rart.Error.Error(), actualError.Error()) @@ -329,11 +346,13 @@ type RunAndOutputTest struct { Name string Component lwcomponent.Component Version string + Content string Signature string Args []string Stdin io.Reader Expected string Error error + Envs []string } var RunAndOutputTests = []RunAndOutputTest{ @@ -341,18 +360,29 @@ var RunAndOutputTests = []RunAndOutputTest{ Name: "OK", Component: mockComponent, Version: "1.0.0", + Content: string(helloWorld), Signature: helloWorldSig, Args: []string{"World"}, Stdin: strings.NewReader("Error"), Expected: "Hello World!\nHello !\n", Error: nil, }, + RunAndOutputTest{ + Name: "env-vars", + Component: mockComponent, + Version: "1.0.0", + Content: string(envVars), + Signature: envVarsSig, + Args: []string{"cdk-init"}, + Expected: "Hello environment test!\n", + Envs: []string{"LW_TEST=environment test"}, + }, } func TestRunAndOutput(t *testing.T) { for _, raot := range RunAndOutputTests { t.Run(raot.Name, func(t *testing.T) { - componentDir, err := ensureMockComponent(raot.Version, raot.Signature) + componentDir, err := ensureMockComponent(raot.Version, raot.Content, raot.Signature) if err != nil { assert.FailNow(t, "Unable to ensureMockComponent") } @@ -360,7 +390,7 @@ func TestRunAndOutput(t *testing.T) { actual := capturer.CaptureOutput(func() { // @afiune fake STDIN - actualError := raot.Component.RunAndOutput(raot.Args) + actualError := raot.Component.RunAndOutput(raot.Args, raot.Envs...) if raot.Error != nil { assert.Equal(t, raot.Error.Error(), actualError.Error()) diff --git a/lwcomponent/executable.go b/lwcomponent/executable.go index e74e8f2b0..7e502cc63 100644 --- a/lwcomponent/executable.go +++ b/lwcomponent/executable.go @@ -45,8 +45,9 @@ func (c Component) RunAndOutput(args []string, envs ...string) error { return c.run(cmd) } -// RunAndReturn runs the command and returns its standard output and standard error -func (c Component) RunAndReturn(args []string, stdin io.Reader) ( +// RunAndReturn runs the command and returns its standard output and standard error, +// the provided environment variables will be accessible by the component +func (c Component) RunAndReturn(args []string, stdin io.Reader, envs ...string) ( stdout string, stderr string, err error, @@ -61,10 +62,7 @@ func (c Component) RunAndReturn(args []string, stdin io.Reader) ( cmd := exec.Command(loc, args...) cmd.Env = os.Environ() - // @afiune a number of components might need to have access to the Lacework API - // we need to figure out a way to pass credentials to the component - // cmd.Env = append(cmd.Env, fmt.Sprintf("LW_ACCOUNT=%s", account)) - // cmd.Env = append(cmd.Env, fmt.Sprintf("LW_TOKEN=%s", token)) + cmd.Env = append(cmd.Env, envs...) cmd.Stdin = stdin cmd.Stdout = &outBuff cmd.Stderr = &errBuff diff --git a/lwcomponent/test_resources/env-vars.sh b/lwcomponent/test_resources/env-vars.sh new file mode 100644 index 000000000..b2f94c336 --- /dev/null +++ b/lwcomponent/test_resources/env-vars.sh @@ -0,0 +1,2 @@ +#!/bin/bash +echo "Hello ${LW_TEST}!" diff --git a/lwcomponent/test_resources/env-vars.sig b/lwcomponent/test_resources/env-vars.sig new file mode 100644 index 000000000..b2e9f166c --- /dev/null +++ b/lwcomponent/test_resources/env-vars.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IApSV1FueUhuVEt6MVJpVktIRE5vR1A3amlNZEJ1WktUWEJLRUZpVFBBRTFidVFVQVlJbUthd1RiWVg4dU1GYXJOc2hQMThVZElySGRhTmxkSE1QeEsxMUsrUEpNdUtoWVE0UTA9CnRydXN0ZWQgY29tbWVudDogMS5SV1FueUhuVEt6MVJpZHFNbDQ1T3U1WGJKQ3JvVjd6b2hFMWpwYmlnVFVHWHlRWTk0QUY1dW80di4xLmRXNTBjblZ6ZEdWa0lHTnZiVzFsYm5RNklBcFNWMVJqYlZsamRqbFFNSGxOY0V4eVZuQTNWM2xJZG14MlJVMDJWbGhKYkZoelVHRTBRVlpWTDFNcmFIbHdVQ3RRZEhCRldFVnZjazVNUzNaUmVUSlNWMFF4VG1OS056TmxVMU5NWVVFMlRucExObkJ2Y3pscGIyUmhiRU5NY3poT1p6ZzlDblJ5ZFhOMFpXUWdZMjl0YldWdWREb2dDbTFpTW1wSlJWWkJVVE5QWmxJdkwwcFJjalV4VnpkUVIzRnJXRGhHYldNMVlVRktjQ3QwUTNKWE9IWm1XbVo2WW5WTU0yWnZlamR0UW1WNk1IcFFkbEJsZG1ReFUwRnJVa2xtU1cxRU1rZGpTa1ZsVUVKM1BUMD0Ka3BoUVlieFp2UUw0cEJzYTdtRVZWMXZwRUYzU0dXWmEvK29UdUN3TEFxRmVwai9ISzFibmJCdlFGY010Vy9STVJKU2lxSFV6TWwwa0ROcXJoSjBqQkE9PQ==