diff --git a/cmd/kosli/expect.go b/cmd/kosli/expect.go deleted file mode 100644 index 1fa07d730..000000000 --- a/cmd/kosli/expect.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "io" - - "github.com/spf13/cobra" -) - -const expectDesc = `All Kosli expect commands.` - -func newExpectCmd(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "expect", - Short: expectDesc, - Long: expectDesc, - Deprecated: "all child commands are deprecated", - } - - // Add subcommands - cmd.AddCommand( - newExpectDeploymentCmd(out), - ) - - return cmd -} diff --git a/cmd/kosli/expectDeployment.go b/cmd/kosli/expectDeployment.go deleted file mode 100644 index 6fa298267..000000000 --- a/cmd/kosli/expectDeployment.go +++ /dev/null @@ -1,108 +0,0 @@ -package main - -import ( - "io" - "net/http" - "net/url" - - "github.com/kosli-dev/cli/internal/requests" - "github.com/spf13/cobra" -) - -const expectDeploymentShortDesc = `Report the expectation of an upcoming deployment of an artifact to an environment. ` - -const expectDeploymentLongDesc = expectDeploymentShortDesc + ` -` + fingerprintDesc - -type expectDeploymentOptions struct { - fingerprintOptions *fingerprintOptions - flowName string - userDataFile string - payload ExpectDeploymentPayload -} - -type ExpectDeploymentPayload struct { - Fingerprint string `json:"artifact_fingerprint"` - Description string `json:"description"` - Environment string `json:"environment"` - UserData interface{} `json:"user_data"` - BuildUrl string `json:"build_url"` -} - -func newExpectDeploymentCmd(out io.Writer) *cobra.Command { - o := new(expectDeploymentOptions) - o.fingerprintOptions = new(fingerprintOptions) - cmd := &cobra.Command{ - Use: "deployment [IMAGE-NAME | FILE-PATH | DIR-PATH]", - Short: expectDeploymentShortDesc, - Long: expectDeploymentLongDesc, - Args: cobra.MaximumNArgs(1), - Deprecated: "deployment expectation is no longer required for compliance.", - PreRunE: func(cmd *cobra.Command, args []string) error { - err := RequireGlobalFlags(global, []string{"Org", "ApiToken"}) - if err != nil { - return ErrorBeforePrintingUsage(cmd, err.Error()) - } - - err = ValidateArtifactArg(args, o.fingerprintOptions.artifactType, o.payload.Fingerprint, false) - if err != nil { - return ErrorBeforePrintingUsage(cmd, err.Error()) - } - return ValidateRegistryFlags(cmd, o.fingerprintOptions) - - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(args) - }, - } - - ci := WhichCI() - cmd.Flags().StringVarP(&o.payload.Fingerprint, "fingerprint", "F", "", fingerprintFlag) - cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) - cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", environmentNameFlag) - cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", deploymentDescriptionFlag) - cmd.Flags().StringVarP(&o.payload.BuildUrl, "build-url", "b", DefaultValue(ci, "build-url"), buildUrlFlag) - cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", deploymentUserDataFlag) - addFingerprintFlags(cmd, o.fingerprintOptions) - addDryRunFlag(cmd) - - err := RequireFlags(cmd, []string{"flow", "build-url", "environment"}) - if err != nil { - logger.Error("failed to configure required flags: %v", err) - } - - return cmd -} - -func (o *expectDeploymentOptions) run(args []string) error { - var err error - if o.payload.Fingerprint == "" { - o.payload.Fingerprint, err = GetSha256Digest(args[0], o.fingerprintOptions, logger) - if err != nil { - return err - } - } - - o.payload.UserData, err = LoadJsonData(o.userDataFile) - if err != nil { - return err - } - - url, err := url.JoinPath(global.Host, "api/v2/deployments", global.Org, o.flowName) - if err != nil { - return err - } - - reqParams := &requests.RequestParams{ - Method: http.MethodPost, - URL: url, - Payload: o.payload, - DryRun: global.DryRun, - Token: global.ApiToken, - } - _, err = kosliClient.Do(reqParams) - if err == nil && !global.DryRun { - logger.Info("expect deployment of artifact %s was reported to: %s", o.payload.Fingerprint, o.payload.Environment) - } - return err -} diff --git a/cmd/kosli/expectDeployment_test.go b/cmd/kosli/expectDeployment_test.go deleted file mode 100644 index ab6d93b41..000000000 --- a/cmd/kosli/expectDeployment_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including a T() method which -// returns the current testing context -type ExpectDeploymentCommandTestSuite struct { - suite.Suite - defaultKosliArguments string - flowName string - envName string - artifactName string - artifactPath string - fingerprint string -} - -func (suite *ExpectDeploymentCommandTestSuite) SetupTest() { - suite.flowName = "expect-deploy" - suite.envName = "expect-deploy-env" - suite.artifactName = "arti" - suite.artifactPath = "testdata/folder1/hello.txt" - global = &GlobalOpts{ - ApiToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY", - Org: "docs-cmd-test-user", - Host: "http://localhost:8001", - } - suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) - - CreateFlow(suite.flowName, suite.T()) - fingerprintOptions := &fingerprintOptions{ - artifactType: "file", - } - var err error - suite.fingerprint, err = GetSha256Digest(suite.artifactPath, fingerprintOptions, logger) - require.NoError(suite.T(), err) - CreateArtifact(suite.flowName, suite.fingerprint, suite.artifactName, suite.T()) - CreateEnv(global.Org, suite.envName, "server", suite.T()) -} - -func (suite *ExpectDeploymentCommandTestSuite) TestExpectDeploymentCmd() { - tests := []cmdTestCase{ - { - name: "expect deployment works (with --fingerprint)", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s --build-url http://www.example.com %s`, - suite.flowName, suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: fmt.Sprintf("Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nexpect deployment of artifact %s was reported to: expect-deploy-env\n", suite.fingerprint), - }, - { - name: "expect deployment works (with --artifact-type)", - cmd: fmt.Sprintf(`expect deployment %s --artifact-type file --flow %s --fingerprint %s --environment %s --build-url http://www.example.com %s`, - suite.artifactPath, suite.flowName, suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: fmt.Sprintf("Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nexpect deployment of artifact %s was reported to: expect-deploy-env\n", suite.fingerprint), - }, - { - name: "expect deployment works with --user-data", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s --build-url http://www.example.com - --user-data testdata/snyk_scan_example.json %s`, - suite.flowName, suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: fmt.Sprintf("Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nexpect deployment of artifact %s was reported to: expect-deploy-env\n", suite.fingerprint), - }, - { - wantError: true, - name: "missing --org flag causes an error", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s --build-url http://www.example.com - --api-token secret`, - suite.flowName, suite.fingerprint, suite.envName), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: --org is not set\nUsage: kosli expect deployment [IMAGE-NAME | FILE-PATH | DIR-PATH] [flags]\n", - }, - { - wantError: true, - name: "missing --api-token flag causes an error", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s --build-url http://www.example.com - --org orgX`, - suite.flowName, suite.fingerprint, suite.envName), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: --api-token is not set\nUsage: kosli expect deployment [IMAGE-NAME | FILE-PATH | DIR-PATH] [flags]\n", - }, - { - wantError: true, - name: "expect deployment fails when --user-data is a non-existing file", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s --build-url http://www.example.com - --user-data non-existing.json %s`, - suite.flowName, suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: open non-existing.json: no such file or directory\n", - }, - { - wantError: true, - name: "expect deployment fails if --flow is missing", - cmd: fmt.Sprintf(`expect deployment --fingerprint %s --environment %s --build-url http://www.example.com %s`, - suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: required flag(s) \"flow\" not set\n", - }, - { - wantError: true, - name: "expect deployment fails if --environment is missing", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --build-url http://www.example.com %s`, - suite.flowName, suite.fingerprint, suite.defaultKosliArguments), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: required flag(s) \"environment\" not set\n", - }, - { - wantError: true, - name: "expect deployment fails if --build-url is missing", - cmd: fmt.Sprintf(`expect deployment --flow %s --fingerprint %s --environment %s %s`, - suite.flowName, suite.fingerprint, suite.envName, suite.defaultKosliArguments), - golden: "Command \"deployment\" is deprecated, deployment expectation is no longer required for compliance.\nError: required flag(s) \"build-url\" not set\n", - }, - } - - runTestCmd(suite.T(), tests) -} - -// In order for 'go test' to run this suite, we need to create -// a normal test function and pass our suite to suite.Run -func TestExpectDeploymentCommandTestSuite(t *testing.T) { - suite.Run(t, new(ExpectDeploymentCommandTestSuite)) -} diff --git a/cmd/kosli/get.go b/cmd/kosli/get.go index dd5905ab3..385f64916 100644 --- a/cmd/kosli/get.go +++ b/cmd/kosli/get.go @@ -19,7 +19,6 @@ func newGetCmd(out io.Writer) *cobra.Command { cmd.AddCommand( newGetApprovalCmd(out), newGetArtifactCmd(out), - newGetDeploymentCmd(out), newGetEnvironmentCmd(out), newGetFlowCmd(out), newGetSnapshotCmd(out), diff --git a/cmd/kosli/getDeployment.go b/cmd/kosli/getDeployment.go deleted file mode 100644 index 51f507a97..000000000 --- a/cmd/kosli/getDeployment.go +++ /dev/null @@ -1,149 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - - "github.com/kosli-dev/cli/internal/output" - "github.com/kosli-dev/cli/internal/requests" - "github.com/spf13/cobra" -) - -const getDeploymentShortDesc = `Get a deployment from a specified flow.` - -const getDeploymentLongDesc = getDeploymentShortDesc + ` -EXPRESSION can be specified as follows: -- flowName - - the latest deployment to flowName, at the time of the request - - e.g., **dashboard** -- flowName#N - - the Nth deployment, counting from 1 - - e.g., **dashboard#453** -- flowName~N - - the Nth deployment behind the latest, at the time of the request - - e.g., **dashboard~56** -` - -const getDeploymentExample = ` -# get previous deployment in a flow -kosli get deployment flowName~1 \ - --api-token yourAPIToken \ - --org orgName - -# get the 10th deployment in a flow -kosli get deployment flowName#10 \ - --api-token yourAPIToken \ - --org orgName - -# get the latest deployment in a flow -kosli get deployment flowName \ - --api-token yourAPIToken \ - --org orgName` - -type getDeploymentOptions struct { - output string -} - -func newGetDeploymentCmd(out io.Writer) *cobra.Command { - o := new(getDeploymentOptions) - cmd := &cobra.Command{ - Use: "deployment EXPRESSION", - Short: getDeploymentShortDesc, - Long: getDeploymentLongDesc, - Example: getDeploymentExample, - Args: cobra.ExactArgs(1), - PreRunE: func(cmd *cobra.Command, args []string) error { - err := RequireGlobalFlags(global, []string{"Org", "ApiToken"}) - if err != nil { - return ErrorBeforePrintingUsage(cmd, err.Error()) - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out, args) - }, - } - - cmd.Flags().StringVarP(&o.output, "output", "o", "table", outputFlag) - - return cmd -} - -func (o *getDeploymentOptions) run(out io.Writer, args []string) error { - flowName, id, err := handleExpressions(args[0]) - if err != nil { - return err - } - url, err := url.JoinPath(global.Host, "api/v2/deployments", global.Org, flowName, strconv.Itoa(id)) - if err != nil { - return err - } - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: url, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - return err - } - - return output.FormattedPrint(response.Body, o.output, out, 0, - map[string]output.FormatOutputFunc{ - "table": printDeploymentAsTable, - "json": output.PrintJson, - }) -} - -func printDeploymentAsTable(raw string, out io.Writer, page int) error { - var deployment map[string]interface{} - err := json.Unmarshal([]byte(raw), &deployment) - if err != nil { - return err - } - - rows := []string{} - rows = append(rows, fmt.Sprintf("ID:\t%d", int64(deployment["deployment_id"].(float64)))) - rows = append(rows, fmt.Sprintf("Artifact fingerprint:\t%s", deployment["artifact_fingerprint"].(string))) - rows = append(rows, fmt.Sprintf("Artifact name:\t%s", deployment["artifact_name"].(string))) - buildURL := "N/A" - if deployment["build_url"] != nil { - buildURL = deployment["build_url"].(string) - } - rows = append(rows, fmt.Sprintf("Build URL:\t%s", buildURL)) - createdAt, err := formattedTimestamp(deployment["created_at"], false) - if err != nil { - return err - } - rows = append(rows, fmt.Sprintf("Created at:\t%s", createdAt)) - rows = append(rows, fmt.Sprintf("Environment:\t%s", deployment["environment"].(string))) - - deploymentState := deployment["running_state"].(map[string]interface{}) - state := deploymentState["state"].(string) - stateTimestamp, err := formattedTimestamp(deploymentState["timestamp"], true) - if err != nil { - return err - } - - stateString := "Unknown" - switch state { - case "deploying": - stateString = "Deploying" - case "running": - stateString = fmt.Sprintf("The artifact running since %s", stateTimestamp) - case "exited": - stateString = fmt.Sprintf("The artifact exited on %s", stateTimestamp) - } - - deploymentRow := fmt.Sprintf("Runtime state:\t%s", - stateString) - rows = append(rows, deploymentRow) - - tabFormattedPrint(out, []string{}, rows) - return nil -} diff --git a/cmd/kosli/getDeployment_test.go b/cmd/kosli/getDeployment_test.go deleted file mode 100644 index 9d503e3a9..000000000 --- a/cmd/kosli/getDeployment_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including a T() method which -// returns the current testing context -type GetDeploymentCommandTestSuite struct { - suite.Suite - defaultKosliArguments string - flowName string - envName string - fingerprint string - artifactPath string -} - -func (suite *GetDeploymentCommandTestSuite) SetupTest() { - suite.flowName = "get-deployment" - suite.envName = "get-deployment-env" - suite.artifactPath = "testdata/folder1/hello.txt" - global = &GlobalOpts{ - ApiToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY", - Org: "docs-cmd-test-user", - Host: "http://localhost:8001", - } - suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) - - CreateFlow(suite.flowName, suite.T()) - fingerprintOptions := &fingerprintOptions{ - artifactType: "file", - } - var err error - suite.fingerprint, err = GetSha256Digest(suite.artifactPath, fingerprintOptions, logger) - require.NoError(suite.T(), err) - CreateArtifact(suite.flowName, suite.fingerprint, "arti-name", suite.T()) - CreateEnv(global.Org, suite.envName, "server", suite.T()) - ExpectDeployment(suite.flowName, suite.fingerprint, suite.envName, suite.T()) - ExpectDeployment(suite.flowName, suite.fingerprint, suite.envName, suite.T()) -} - -func (suite *GetDeploymentCommandTestSuite) TestGetDeploymentCmd() { - tests := []cmdTestCase{ - { - wantError: true, - name: "providing more than one argument fails", - cmd: fmt.Sprintf(`get deployment %s xxx %s`, suite.flowName, suite.defaultKosliArguments), - golden: "Error: accepts 1 arg(s), received 2\n", - }, - { - wantError: true, - name: "providing no arguments fails", - cmd: fmt.Sprintf(`get deployment %s`, suite.defaultKosliArguments), - golden: "Error: accepts 1 arg(s), received 0\n", - }, - { - wantError: true, - name: "get deployment fails when --api-token flag is missing", - cmd: `get deployment ` + suite.flowName + `#1 --org foo --host bar`, - golden: "Error: --api-token is not set\n" + - "Usage: kosli get deployment EXPRESSION [flags]\n", - }, - { - wantError: true, - name: "get deployment fails when flow does not exist", - cmd: `get deployment foo#1` + suite.defaultKosliArguments, - goldenRegex: "^Error: Flow named 'foo' does not exist for organization 'docs-cmd-test-user'", - }, - { - wantError: true, - name: "get deployment fails when deployment does not exist", - cmd: fmt.Sprintf(`get deployment %s#20 %s`, suite.flowName, suite.defaultKosliArguments), - golden: "Error: Deployment number '20' does not exist in flow 'get-deployment' belonging to organization 'docs-cmd-test-user'\n", - }, - { - name: "get deployment works with the # expression", - cmd: fmt.Sprintf(`get deployment %s#1 %s`, suite.flowName, suite.defaultKosliArguments), - goldenFile: "output/get/get-deployment.txt", - }, - { - name: "get deployment works with the ~ expression", - cmd: fmt.Sprintf(`get deployment %s~1 %s`, suite.flowName, suite.defaultKosliArguments), - goldenFile: "output/get/get-deployment.txt", - }, - { - name: "get deployment works with just the flow name", - cmd: fmt.Sprintf(`get deployment %s %s`, suite.flowName, suite.defaultKosliArguments), - goldenFile: "output/get/get-deployment-latest.txt", - }, - { - name: "get deployment works with --output json", - cmd: fmt.Sprintf(`get deployment %s --output json %s`, suite.flowName, suite.defaultKosliArguments), - }, - } - - runTestCmd(suite.T(), tests) -} - -// In order for 'go test' to run this suite, we need to create -// a normal test function and pass our suite to suite.Run -func TestGetDeploymentCommandTestSuite(t *testing.T) { - suite.Run(t, new(GetDeploymentCommandTestSuite)) -} diff --git a/cmd/kosli/list.go b/cmd/kosli/list.go index 5b223e1b6..a47e69064 100644 --- a/cmd/kosli/list.go +++ b/cmd/kosli/list.go @@ -36,7 +36,6 @@ func newListCmd(out io.Writer) *cobra.Command { cmd.AddCommand( newListApprovalsCmd(out), newListArtifactsCmd(out), - newListDeploymentsCmd(out), newListEnvironmentsCmd(out), newListFlowsCmd(out), newListSnapshotsCmd(out), diff --git a/cmd/kosli/listDeployments.go b/cmd/kosli/listDeployments.go deleted file mode 100644 index 9382010f6..000000000 --- a/cmd/kosli/listDeployments.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/kosli-dev/cli/internal/output" - "github.com/kosli-dev/cli/internal/requests" - "github.com/spf13/cobra" -) - -const listDeploymentsShortDesc = `List deployments in a flow.` - -const listDeploymentsLongDesc = listDeploymentsShortDesc + ` -The results are paginated and ordered from latest to oldest. -By default, the page limit is 15 deployments per page. -` -const listDeploymentsExample = ` -# list the last 15 deployments for a flow: -kosli list deployments \ - --flow yourFlowName \ - --api-token yourAPIToken \ - --org yourOrgName - -# list the last 30 deployments for a flow: -kosli list deployments \ - --flow yourFlowName \ - --page-limit 30 \ - --api-token yourAPIToken \ - --org yourOrgName - -# list the last 30 deployments for a flow (in JSON): -kosli list deployments \ - --flow yourFlowName \ - --page-limit 30 \ - --api-token yourAPIToken \ - --org yourOrgName \ - --output json -` - -type listDeploymentsOptions struct { - listOptions - flowName string -} - -func newListDeploymentsCmd(out io.Writer) *cobra.Command { - o := new(listDeploymentsOptions) - cmd := &cobra.Command{ - Use: "deployments", - Aliases: []string{"deployment", "deploy"}, - Short: listDeploymentsShortDesc, - Long: listDeploymentsLongDesc, - Example: listDeploymentsExample, - Args: cobra.NoArgs, - PreRunE: func(cmd *cobra.Command, args []string) error { - err := RequireGlobalFlags(global, []string{"Org", "ApiToken"}) - if err != nil { - return ErrorBeforePrintingUsage(cmd, err.Error()) - } - - return o.validate(cmd) - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - - cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) - addListFlags(cmd, &o.listOptions) - - err := RequireFlags(cmd, []string{"flow"}) - if err != nil { - logger.Error("failed to configure required flags: %v", err) - } - - return cmd -} - -func (o *listDeploymentsOptions) run(out io.Writer) error { - url := fmt.Sprintf("%s/api/v2/deployments/%s/%s?page=%d&per_page=%d", - global.Host, global.Org, o.flowName, o.pageNumber, o.pageLimit) - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: url, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - return err - } - - return output.FormattedPrint(response.Body, o.output, out, o.pageNumber, - map[string]output.FormatOutputFunc{ - "table": printDeploymentsListAsTable, - "json": output.PrintJson, - }) -} - -func printDeploymentsListAsTable(raw string, out io.Writer, page int) error { - - var deployments []map[string]interface{} - err := json.Unmarshal([]byte(raw), &deployments) - if err != nil { - return err - } - - if len(deployments) == 0 { - msg := "No deployments were found" - if page != 1 { - msg = fmt.Sprintf("%s at page number %d", msg, page) - } - logger.Info(msg + ".") - return nil - } - - header := []string{"ID", "ARTIFACT", "ENVIRONMENT", "REPORTED_AT"} - rows := []string{} - for _, deployment := range deployments { - deploymentId := int(deployment["deployment_id"].(float64)) - artifactName := deployment["artifact_name"].(string) - artifactDigest := deployment["artifact_fingerprint"].(string) - environment := deployment["environment"].(string) - createdAt, err := formattedTimestamp(deployment["created_at"], true) - if err != nil { - return err - } - row := fmt.Sprintf("%d\tName: %s\t%s\t%s", deploymentId, artifactName, environment, createdAt) - rows = append(rows, row) - row = fmt.Sprintf("\tFingerprint: %s\t\t", artifactDigest) - rows = append(rows, row) - rows = append(rows, "\t\t\t") - } - tabFormattedPrint(out, header, rows) - - return nil -} diff --git a/cmd/kosli/listDeployments_test.go b/cmd/kosli/listDeployments_test.go deleted file mode 100644 index 6d31b9973..000000000 --- a/cmd/kosli/listDeployments_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including a T() method which -// returns the current testing context -type ListDeploymentsCommandTestSuite struct { - suite.Suite - defaultKosliArguments string - flowName1 string - flowName2 string - artifactName string - artifactPath string - fingerprint string - envName string -} - -func (suite *ListDeploymentsCommandTestSuite) SetupTest() { - suite.flowName1 = "list-deployments-empty" - suite.flowName2 = "list-deployments" - suite.envName = "list-deployments-env" - suite.artifactName = "arti" - suite.artifactPath = "testdata/folder1/hello.txt" - global = &GlobalOpts{ - ApiToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6ImNkNzg4OTg5In0.e8i_lA_QrEhFncb05Xw6E_tkCHU9QfcY4OLTVUCHffY", - Org: "docs-cmd-test-user", - Host: "http://localhost:8001", - } - suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) - CreateFlow(suite.flowName1, suite.T()) - CreateFlow(suite.flowName2, suite.T()) - fingerprintOptions := &fingerprintOptions{ - artifactType: "file", - } - var err error - suite.fingerprint, err = GetSha256Digest(suite.artifactPath, fingerprintOptions, logger) - require.NoError(suite.T(), err) - CreateArtifact(suite.flowName2, suite.fingerprint, suite.artifactName, suite.T()) - CreateEnv(global.Org, suite.envName, "server", suite.T()) - ExpectDeployment(suite.flowName2, suite.fingerprint, suite.envName, suite.T()) -} - -func (suite *ListDeploymentsCommandTestSuite) TestListDeploymentsCmd() { - tests := []cmdTestCase{ - { - wantError: true, - name: "missing flow flag causes an error", - cmd: fmt.Sprintf(`list deployments %s`, suite.defaultKosliArguments), - golden: "Error: required flag(s) \"flow\" not set\n", - }, - { - wantError: true, - name: "non-existing flow causes an error", - cmd: fmt.Sprintf(`list deployments --flow non-existing %s`, suite.defaultKosliArguments), - goldenRegex: "^Error: Flow named 'non-existing' does not exist for organization 'docs-cmd-test-user'", - }, - // TODO: the correct error is overwritten by the hack flag value check in root.go - { - wantError: true, - name: "negative page number causes an error", - cmd: fmt.Sprintf(`list deployments --flow foo --page -1 %s`, suite.defaultKosliArguments), - golden: "Error: flag '--page' has value '-1' which is illegal\n", - }, - { - wantError: true, - name: "negative page limit causes an error", - cmd: fmt.Sprintf(`list deployments --flow foo --page-limit -1 %s`, suite.defaultKosliArguments), - golden: "Error: flag '--page-limit' has value '-1' which is illegal\n", - }, - { - wantError: true, - name: "missing --api-token fails", - cmd: fmt.Sprintf(`list deployments --flow %s --org orgX`, suite.flowName1), - golden: "Error: --api-token is not set\nUsage: kosli list deployments [flags]\n", - }, - { - name: "listing deployments on an empty flow works", - cmd: fmt.Sprintf(`list deployments --flow %s %s`, suite.flowName1, suite.defaultKosliArguments), - golden: "No deployments were found.\n", - }, - { - name: "listing second page of deployments on an empty flow works", - cmd: fmt.Sprintf(`list deployments --flow %s --page 2 %s`, suite.flowName1, suite.defaultKosliArguments), - golden: "No deployments were found at page number 2.\n", - }, - { - name: "listing deployments on an empty flow with --output json works", - cmd: fmt.Sprintf(`list deployments --flow %s --output json %s`, suite.flowName1, suite.defaultKosliArguments), - goldenJson: []jsonCheck{{"", "[]"}}, - }, - { - name: "listing deployments on a flow works", - cmd: fmt.Sprintf(`list deployments --flow %s %s`, suite.flowName2, suite.defaultKosliArguments), - goldenFile: "output/list/list-deployments.txt", - }, - } - - runTestCmd(suite.T(), tests) -} - -// In order for 'go test' to run this suite, we need to create -// a normal test function and pass our suite to suite.Run -func TestListDeploymentsCommandTestSuite(t *testing.T) { - suite.Run(t, new(ListDeploymentsCommandTestSuite)) -} diff --git a/cmd/kosli/listSnapshots.go b/cmd/kosli/listSnapshots.go index b3249a676..de2f2810e 100644 --- a/cmd/kosli/listSnapshots.go +++ b/cmd/kosli/listSnapshots.go @@ -171,7 +171,7 @@ func printEnvironmentEventsLogAsTable(raw string, out io.Writer, page int) error logger.Info(msg + ".") return nil } - header := []string{"SNAPSHOT", "EVENT", "FLOW", "DEPLOYMENTS"} + header := []string{"SNAPSHOT", "EVENT", "FLOW"} rows := []string{} for _, event := range events { snapshotIndex := int(event["snapshot_index"].(float64)) @@ -183,13 +183,8 @@ func printEnvironmentEventsLogAsTable(raw string, out io.Writer, page int) error return err } flow := event["pipeline"].(string) - deploymentsList := event["deployments"].([]interface{}) - deployments := "" - for _, deployment := range deploymentsList { - deployments += fmt.Sprintf("#%d ", int64(deployment.(float64))) - } - row := fmt.Sprintf("#%d\tArtifact: %s\t%s\t%s", snapshotIndex, artifactName, flow, deployments) + row := fmt.Sprintf("#%d\tArtifact: %s\t%s", snapshotIndex, artifactName, flow) rows = append(rows, row) row = fmt.Sprintf("\tFingerprint: %s\t\t", fingerprint) rows = append(rows, row) diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index 5f6431e2d..0e44586f8 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -110,7 +110,6 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file, newestCommitFlag = "[defaulted] The source commit sha for the newest change in the deployment. Can be any commit-ish." repoRootFlag = "[defaulted] The directory where the source git repository is available." approvalDescriptionFlag = "[optional] The approval description." - deploymentDescriptionFlag = "[optional] The deployment description." jiraBaseUrlFlag = "The base url for the jira project, e.g. 'https://kosli.atlassian.net'" jiraUsernameFlag = "Jira username (for Jira Cloud)" jiraAPITokenFlag = "Jira API token (for Jira Cloud)" @@ -129,7 +128,6 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file, useEmptyTemplateFlag = "Use an empty template for the flow creation without specifying a file. Cannot be used together with --template or --template-file" approvalUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to the approval." attestationUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to the attestation." - deploymentUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to the deployment." trailUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to the flow trail." gitCommitFlag = "[defaulted] The git commit from which the artifact was created. (defaulted in some CIs: https://docs.kosli.com/ci-defaults, otherwise defaults to HEAD )." buildUrlFlag = "The url of CI pipeline that built the artifact. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." @@ -358,7 +356,6 @@ func newRootCmd(out, errOut io.Writer, args []string) (*cobra.Command, error) { newFingerprintCmd(out), newAssertCmd(out), newStatusCmd(out), - newExpectCmd(out), newSearchCmd(out), newCompletionCmd(out), // Hidden documentation generator command: 'kosli docs' diff --git a/cmd/kosli/testHelpers.go b/cmd/kosli/testHelpers.go index 824038383..aa33165b7 100644 --- a/cmd/kosli/testHelpers.go +++ b/cmd/kosli/testHelpers.go @@ -470,21 +470,6 @@ func EnableBeta(t *testing.T) { require.NoError(t, err, "beta should be enabled without error") } -// ExpectDeployment reports a deployment expectation of a given artifact to the server -func ExpectDeployment(flowName, fingerprint, envName string, t *testing.T) { - t.Helper() - o := &expectDeploymentOptions{ - flowName: flowName, - payload: ExpectDeploymentPayload{ - Fingerprint: fingerprint, - Environment: envName, - BuildUrl: "https://example.com", - }, - } - err := o.run([]string{}) - require.NoError(t, err, "deployment should be expected without error") -} - // CreateEnv creates an env on the server func CreateEnv(org, envName, envType string, t *testing.T) { t.Helper() diff --git a/cmd/kosli/testdata/output/get/get-deployment-latest.txt b/cmd/kosli/testdata/output/get/get-deployment-latest.txt deleted file mode 100644 index 3dd9d3170..000000000 --- a/cmd/kosli/testdata/output/get/get-deployment-latest.txt +++ /dev/null @@ -1,7 +0,0 @@ -ID: 2 -Artifact fingerprint: fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02 -Artifact name: arti-name -Build URL: https://example.com -Created at: Sat, 16 Jan 2016 00:00:00 UTC • 2016-01-16 -Environment: get-deployment-env -Runtime state: Deploying \ No newline at end of file diff --git a/cmd/kosli/testdata/output/get/get-deployment.txt b/cmd/kosli/testdata/output/get/get-deployment.txt deleted file mode 100644 index d9708902d..000000000 --- a/cmd/kosli/testdata/output/get/get-deployment.txt +++ /dev/null @@ -1,7 +0,0 @@ -ID: 1 -Artifact fingerprint: fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02 -Artifact name: arti-name -Build URL: https://example.com -Created at: Sat, 16 Jan 2016 00:00:00 UTC • 2016-01-16 -Environment: get-deployment-env -Runtime state: Deploying \ No newline at end of file diff --git a/cmd/kosli/testdata/output/list/list-deployments.txt b/cmd/kosli/testdata/output/list/list-deployments.txt deleted file mode 100644 index 224875ad5..000000000 --- a/cmd/kosli/testdata/output/list/list-deployments.txt +++ /dev/null @@ -1,4 +0,0 @@ -ID ARTIFACT ENVIRONMENT REPORTED_AT -1 Name: arti list-deployments-env Sat, 16 Jan 2016 00:00:00 UTC - Fingerprint: fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02 - diff --git a/docs.kosli.com/content/faq/_index.md b/docs.kosli.com/content/faq/_index.md index 209f8e999..9358589a6 100644 --- a/docs.kosli.com/content/faq/_index.md +++ b/docs.kosli.com/content/faq/_index.md @@ -41,12 +41,11 @@ It most likely means you misspelled a flag. ## "unknown command" errors E.g. ``` -kosli expect deploymenct abc.exe --artifact-type file -Error: unknown command: deploymenct -available subcommands are: deployment +kosli get artefact flowName/artifactName +Error: unknown command: artefact ``` -Note that there is a typo in deploymen**c**t. +Note that there is a typo in **artefact** (should be **artifact**). This error will pop up if you're trying to use a command that is not present in the version of the kosli CLI you are using. ## zsh: no such user or named directory diff --git a/docs.kosli.com/content/integrations/actions.md b/docs.kosli.com/content/integrations/actions.md index fe205e6cd..3af5a9cfa 100644 --- a/docs.kosli.com/content/integrations/actions.md +++ b/docs.kosli.com/content/integrations/actions.md @@ -66,15 +66,6 @@ Custom webhook notifications empower you to implement automation workflows for " "html_url": "https://app.kosli.com/cyber-dojo/runner/719defb995c86ad7c406ad74258fe98b9ebd71dfa80cd786870c967cb6c1f08d", "api_url": "https://app.kosli.com/api/v2/artifacts/cyber-dojo/runner/fingerprint/719defb995c86ad7c406ad74258fe98b9ebd71dfa80cd786870c967cb6c1f08d", "build_url": "https://github.com/cyber-dojo/runner/actions/runs/5891969166", - "deployments": [ - { - "number": "44", - "timestamp": "1692618644", - "build_url": "https://github.com/cyber-dojo/runner/actions/runs/5891969166", - "html_url": "https://app.kosli.com/cyber-dojo/flows/runner/deployments/44", - "api_url": "https://app.kosli.com/api/v2/deployments/cyber-dojo/runner/44" - } - ], "approvals": [ { "number": "42", diff --git a/docs.kosli.com/content/tutorials/following_a_git_commit_to_runtime_environments.md b/docs.kosli.com/content/tutorials/following_a_git_commit_to_runtime_environments.md index 6b119311d..2e4b122d4 100644 --- a/docs.kosli.com/content/tutorials/following_a_git_commit_to_runtime_environments.md +++ b/docs.kosli.com/content/tutorials/following_a_git_commit_to_runtime_environments.md @@ -137,9 +137,7 @@ Let's look at this output in detail: * **History**: * **CI pipeline events** * The artifact was **created** on the 22nd August at 11:35:00 CEST. - * The artifact has `branch-coverage` **evidence**. - * The artifact was **deployed** to [aws-beta](https://app.kosli.com/cyber-dojo/flows/runner/deployments/18) on 22nd August 11:37:17 CEST, and to [aws-prod](https://app.kosli.com/cyber-dojo/flows/runner/deployments/19) - a minute later. + * The artifact has `branch-coverage` **evidence**. * **Runtime environment events** * The artifact was reported **running** in both environments. * The artifact's number of running instances **scaled down**. diff --git a/docs.kosli.com/content/tutorials/tracing_a_production_incident_back_to_git_commits.md b/docs.kosli.com/content/tutorials/tracing_a_production_incident_back_to_git_commits.md index 59f7229f3..9545430ae 100644 --- a/docs.kosli.com/content/tutorials/tracing_a_production_incident_back_to_git_commits.md +++ b/docs.kosli.com/content/tutorials/tracing_a_production_incident_back_to_git_commits.md @@ -43,8 +43,8 @@ kosli log env aws-prod At the time this tutorial was written the output of this command displayed the first page of 177 snapshots. You will see the first page of considerably more than 177 snapshots because -`aws-prod` has moved on since this incident (it has been resolved with new -commits which have created new deployments). +`aws-prod` has moved on since this incident (it has been resolved with new +commits). To limit the output you can set the interval for the command: ```shell {.command} @@ -54,15 +54,15 @@ kosli log env aws-prod --interval 176..177 The output should be: ```plaintext {.light-console} -SNAPSHOT EVENT FLOW DEPLOYMENTS -#177 Artifact: 274425519734.dkr.ecr.eu-central-1.amazonaws.com/creator:31dee35 creator #87 - Fingerprint: 5d1c926530213dadd5c9fcbf59c8822da56e32a04b0f9c774d7cdde3cf6ba66d - Description: 1 instance stopped running (from 1 to 0). - Reported at: Tue, 06 Sep 2022 16:53:28 CEST - -#176 Artifact: 274425519734.dkr.ecr.eu-central-1.amazonaws.com/creator:b7a5908 creator #89 - Fingerprint: 860ad172ace5aee03e6a1e3492a88b3315ecac2a899d4f159f43ca7314290d5a - Description: 1 instance started running (from 0 to 1). +SNAPSHOT EVENT FLOW +#177 Artifact: 274425519734.dkr.ecr.eu-central-1.amazonaws.com/creator:31dee35 creator + Fingerprint: 5d1c926530213dadd5c9fcbf59c8822da56e32a04b0f9c774d7cdde3cf6ba66d + Description: 1 instance stopped running (from 1 to 0). + Reported at: Tue, 06 Sep 2022 16:53:28 CEST + +#176 Artifact: 274425519734.dkr.ecr.eu-central-1.amazonaws.com/creator:b7a5908 creator + Fingerprint: 860ad172ace5aee03e6a1e3492a88b3315ecac2a899d4f159f43ca7314290d5a + Description: 1 instance started running (from 0 to 1). Reported at: Tue, 06 Sep 2022 16:52:28 CEST ``` @@ -92,8 +92,6 @@ Build URL: https://github.com/cyber-dojo/creator/actions/runs/3001102984 State: COMPLIANT History: Artifact created Tue, 06 Sep 2022 16:48:07 CEST - Deployment #88 to aws-beta environment Tue, 06 Sep 2022 16:49:59 CEST - Deployment #89 to aws-prod environment Tue, 06 Sep 2022 16:51:12 CEST Started running in aws-beta#196 environment Tue, 06 Sep 2022 16:51:42 CEST Started running in aws-prod#176 environment Tue, 06 Sep 2022 16:52:28 CEST ```