From 4c5767eb936ea6e3f99e66157ad1e33354779ad8 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Thu, 16 Nov 2023 11:57:20 +0100 Subject: [PATCH 01/16] Add environment flag to reportApproval command --- cmd/kosli/cli_utils.go | 14 ++++++++++++++ cmd/kosli/reportApproval.go | 29 ++++++++++++++++++++++++++++- cmd/kosli/root.go | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cmd/kosli/cli_utils.go b/cmd/kosli/cli_utils.go index 92a46ed8e..6f5150ba5 100644 --- a/cmd/kosli/cli_utils.go +++ b/cmd/kosli/cli_utils.go @@ -192,6 +192,20 @@ func MuXRequiredFlags(cmd *cobra.Command, flagNames []string, atLeastOne bool) e return nil } +func RequireAtLeastOneOfFlags(cmd *cobra.Command, flagNames []string) error { + flagsSet := 0 + for _, name := range flagNames { + flag := cmd.Flags().Lookup(name) + if flag.Changed { + flagsSet++ + } + } + if flagsSet == 0 { + return fmt.Errorf("at least one of %s is required", JoinFlagNames(flagNames)) + } + return nil +} + // JoinFlagNames returns a comma-separated string of flag names with "--" prefix // from a list of plain names func JoinFlagNames(flagNames []string) string { diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 8398f0d5f..2ceb44c26 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -39,6 +39,19 @@ kosli report approval \ --org yourOrgName \ --flow yourFlowName \ --fingerprint yourArtifactFingerprint + +# Report that an artifact with a provided fingerprint (sha256) has been approved +# for deployment to a specific environment. +# The approval is for all commits from the previous approval of an artifact +# to this environment up to the current commit. +kosli report approval \ + --api-token yourAPIToken \ + --description "An optional description for the approval" \ + --approver username \ + --org yourOrgName \ + --flow yourFlowName \ + --fingerprint yourArtifactFingerprint \ + --environment yourEnvironmentName ` type reportApprovalOptions struct { @@ -54,6 +67,7 @@ type reportApprovalOptions struct { type ApprovalPayload struct { ArtifactFingerprint string `json:"artifact_fingerprint"` + Environment string `json:"environment,omitempty"` Description string `json:"description"` CommitList []string `json:"src_commit_list"` Reviews []map[string]string `json:"approvals"` @@ -74,6 +88,11 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { return ErrorBeforePrintingUsage(cmd, err.Error()) } + err = RequireAtLeastOneOfFlags(cmd, []string{"environment", "oldest-commit"}) + if err != nil { + return err + } + err = ValidateArtifactArg(args, o.fingerprintOptions.artifactType, o.payload.ArtifactFingerprint, false) if err != nil { return ErrorBeforePrintingUsage(cmd, err.Error()) @@ -86,6 +105,7 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&o.payload.ArtifactFingerprint, "fingerprint", "F", "", fingerprintFlag) + cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", environmentNameFlag) cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) @@ -96,7 +116,14 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { addFingerprintFlags(cmd, o.fingerprintOptions) addDryRunFlag(cmd) - err := RequireFlags(cmd, []string{"flow", "oldest-commit"}) + err := DeprecateFlags(cmd, map[string]string{ + "oldest-commit": "use --environment and oldest commit will be calculated", + }) + if err != nil { + logger.Error("failed to configure deprecated flags: %v", err) + } + + err = RequireFlags(cmd, []string{"flow"}) if err != nil { logger.Error("failed to configure required flags: %v", err) } diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index fef78c944..1aad0a5f6 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -77,7 +77,7 @@ The service principal needs to have the following permissions: newEnvTypeFlag = "The type of environment. Valid types are: [K8S, ECS, server, S3, lambda, docker]." envAllowListFlag = "The environment name for which the artifact is allowlisted." reasonFlag = "The reason why this artifact is allowlisted." - oldestCommitFlag = "The source commit sha for the oldest change in the deployment." + oldestCommitFlag = "[conditional] The source commit sha for the oldest change in the deployment. Only required if you don't specify '--environment'." newestCommitFlag = "[defaulted] The source commit sha for the newest change in the deployment." repoRootFlag = "[defaulted] The directory where the source git repository is available." approvalDescriptionFlag = "[optional] The approval description." From 06d79ae48ad7c803977c0569c40a8a0f272b77fd Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Fri, 17 Nov 2023 11:23:40 +0100 Subject: [PATCH 02/16] Approval: only send newest commit in commit list. Added sending oldest commit in seperate parameter --- cmd/kosli/reportApproval.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 2ceb44c26..d723425e2 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -66,12 +66,14 @@ type reportApprovalOptions struct { } type ApprovalPayload struct { - ArtifactFingerprint string `json:"artifact_fingerprint"` - Environment string `json:"environment,omitempty"` - Description string `json:"description"` - CommitList []string `json:"src_commit_list"` - Reviews []map[string]string `json:"approvals"` - UserData interface{} `json:"user_data"` + ArtifactFingerprint string `json:"artifact_fingerprint"` + Environment string `json:"environment,omitempty"` + Description string `json:"description"` + CommitList []string `json:"src_commit_list"` + // NewestCommit string `json:"newest_commit"` + OldestCommit string `json:"oldest_commit,omitempty"` + Reviews []map[string]string `json:"approvals"` + UserData interface{} `json:"user_data"` } func newReportApprovalCmd(out io.Writer) *cobra.Command { @@ -109,7 +111,7 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) - cmd.Flags().StringVar(&o.oldestSrcCommit, "oldest-commit", "", oldestCommitFlag) + cmd.Flags().StringVar(&o.payload.OldestCommit, "oldest-commit", "", oldestCommitFlag) cmd.Flags().StringVar(&o.newestSrcCommit, "newest-commit", "HEAD", newestCommitFlag) cmd.Flags().StringVar(&o.srcRepoRoot, "repo-root", ".", repoRootFlag) cmd.Flags().StringVar(&o.approver, "approver", "", approverFlag) @@ -145,10 +147,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { return err } - o.payload.CommitList, err = o.payloadCommitList() - if err != nil { - return err - } + o.payload.CommitList = []string{o.newestSrcCommit} // o.payloadCommitList() url := fmt.Sprintf("%s/api/v2/approvals/%s/%s", global.Host, global.Org, o.flowName) From d8f5d05204c8d8f7c764a5fdf80f627f75fb0a57 Mon Sep 17 00:00:00 2001 From: Sami Alajrami Date: Fri, 17 Nov 2023 12:26:30 +0100 Subject: [PATCH 03/16] parse the input oldest and newset commit to verify they exist and get their sha1 hashes --- cmd/kosli/reportApproval.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index d723425e2..eceb5edca 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -66,14 +66,14 @@ type reportApprovalOptions struct { } type ApprovalPayload struct { - ArtifactFingerprint string `json:"artifact_fingerprint"` - Environment string `json:"environment,omitempty"` - Description string `json:"description"` - CommitList []string `json:"src_commit_list"` - // NewestCommit string `json:"newest_commit"` - OldestCommit string `json:"oldest_commit,omitempty"` - Reviews []map[string]string `json:"approvals"` - UserData interface{} `json:"user_data"` + ArtifactFingerprint string `json:"artifact_fingerprint"` + Environment string `json:"environment,omitempty"` + Description string `json:"description"` + CommitList []string `json:"src_commit_list"` + NewestCommit string `json:"newest_commit"` + OldestCommit string `json:"oldest_commit,omitempty"` + Reviews []map[string]string `json:"approvals"` + UserData interface{} `json:"user_data"` } func newReportApprovalCmd(out io.Writer) *cobra.Command { @@ -111,7 +111,7 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) - cmd.Flags().StringVar(&o.payload.OldestCommit, "oldest-commit", "", oldestCommitFlag) + cmd.Flags().StringVar(&o.oldestSrcCommit, "oldest-commit", "", oldestCommitFlag) cmd.Flags().StringVar(&o.newestSrcCommit, "newest-commit", "HEAD", newestCommitFlag) cmd.Flags().StringVar(&o.srcRepoRoot, "repo-root", ".", repoRootFlag) cmd.Flags().StringVar(&o.approver, "approver", "", approverFlag) @@ -146,8 +146,24 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { if err != nil { return err } + gitView, err := gitview.New(o.srcRepoRoot) + if err != nil { + return err + } + + o.payload.NewestCommit, err = gitView.ResolveRevision(o.newestSrcCommit) + if err != nil { + return err + } + + if o.oldestSrcCommit != "" { + o.payload.OldestCommit, err = gitView.ResolveRevision(o.oldestSrcCommit) + if err != nil { + return err + } + } - o.payload.CommitList = []string{o.newestSrcCommit} // o.payloadCommitList() + o.payload.CommitList = []string{} // o.payloadCommitList() url := fmt.Sprintf("%s/api/v2/approvals/%s/%s", global.Host, global.Org, o.flowName) From bc13e45d3b534ac2defb0aa4791b9a5b62a34776 Mon Sep 17 00:00:00 2001 From: Arstanaly Rysbekov Date: Fri, 17 Nov 2023 13:47:40 +0100 Subject: [PATCH 04/16] Commands: add environment flag for request approval. --- cmd/kosli/reportApproval.go | 20 ++++++++++--------- cmd/kosli/requestApproval.go | 37 +++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index eceb5edca..a926f4343 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -11,9 +11,11 @@ import ( "github.com/spf13/cobra" ) -const reportApprovalShortDesc = `Report an approval of deploying an artifact to Kosli. ` -const reportApprovalLongDesc = reportApprovalShortDesc + ` +const ( + reportApprovalShortDesc = `Report an approval of deploying an artifact to Kosli. ` + reportApprovalLongDesc = reportApprovalShortDesc + ` ` + fingerprintDesc +) const reportApprovalExample = ` # Report that a file type artifact has been approved for deployment. @@ -118,14 +120,14 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { addFingerprintFlags(cmd, o.fingerprintOptions) addDryRunFlag(cmd) - err := DeprecateFlags(cmd, map[string]string{ - "oldest-commit": "use --environment and oldest commit will be calculated", - }) - if err != nil { - logger.Error("failed to configure deprecated flags: %v", err) - } + // err := DeprecateFlags(cmd, map[string]string{ + // "oldest-commit": "use --environment and oldest commit will be calculated", + // }) + // if err != nil { + // logger.Error("failed to configure deprecated flags: %v", err) + // } - err = RequireFlags(cmd, []string{"flow"}) + err := RequireFlags(cmd, []string{"flow"}) if err != nil { logger.Error("failed to configure required flags: %v", err) } diff --git a/cmd/kosli/requestApproval.go b/cmd/kosli/requestApproval.go index e7c4a58fa..cfc46ebdd 100644 --- a/cmd/kosli/requestApproval.go +++ b/cmd/kosli/requestApproval.go @@ -6,10 +6,12 @@ import ( "github.com/spf13/cobra" ) -const requestApprovalShortDesc = `Request an approval of a deployment of an artifact in Kosli. ` -const requestApprovalLongDesc = requestApprovalShortDesc + ` +const ( + requestApprovalShortDesc = `Request an approval of a deployment of an artifact in Kosli. ` + requestApprovalLongDesc = requestApprovalShortDesc + ` The request should be reviewed in the Kosli UI. ` + fingerprintDesc +) const requestApprovalExample = ` # Request that a file type artifact needs approval. @@ -23,7 +25,7 @@ kosli request approval FILE.tgz \ --org yourOrgName \ --flow yourFlowName -# Request and approval for an artifact with a provided fingerprint (sha256). +# Request an approval for an artifact with a provided fingerprint (sha256). # The approval is for the last 5 git commits kosli request approval \ --api-token yourAPIToken \ @@ -32,7 +34,19 @@ kosli request approval \ --oldest-commit $(git rev-parse HEAD~5) \ --org yourOrgName \ --flow yourFlowName \ - --fingerprint yourArtifactFingerprint + --fingerprint yourArtifactFingerprint + +# Request an approval for an artifact with a provided fingerprint (sha256) +# for deployment to a specific environment. +# The approval is for all commits from the previous approval of an artifact +# to this environment up to the current commit. +kosli request approval \ + --api-token yourAPIToken \ + --description "An optional description for the approval" \ + --org yourOrgName \ + --flow yourFlowName \ + --fingerprint yourArtifactFingerprint \ + --environment yourEnvironmentName ` func newRequestApprovalCmd(out io.Writer) *cobra.Command { @@ -49,6 +63,11 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command { return ErrorBeforePrintingUsage(cmd, err.Error()) } + err = RequireAtLeastOneOfFlags(cmd, []string{"environment", "oldest-commit"}) + if err != nil { + return err + } + err = ValidateArtifactArg(args, o.fingerprintOptions.artifactType, o.payload.ArtifactFingerprint, false) if err != nil { return ErrorBeforePrintingUsage(cmd, err.Error()) @@ -61,6 +80,7 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&o.payload.ArtifactFingerprint, "fingerprint", "F", "", fingerprintFlag) + cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", environmentNameFlag) cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) @@ -70,7 +90,14 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command { addFingerprintFlags(cmd, o.fingerprintOptions) addDryRunFlag(cmd) - err := RequireFlags(cmd, []string{"flow", "oldest-commit"}) + // err := DeprecateFlags(cmd, map[string]string{ + // "oldest-commit": "use --environment and oldest commit will be calculated", + // }) + // if err != nil { + // logger.Error("failed to configure deprecated flags: %v", err) + // } + + err := RequireFlags(cmd, []string{"flow"}) if err != nil { logger.Error("failed to configure required flags: %v", err) } From 993a9a6be532ed4e7a87aab4df90a7f1b3168d63 Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Mon, 20 Nov 2023 10:01:42 +0100 Subject: [PATCH 05/16] Updated approval documentation and CLI reference --- cmd/kosli/reportApproval.go | 40 +++++++++---------- cmd/kosli/requestApproval.go | 38 +++++++++--------- cmd/kosli/root.go | 4 +- .../content/getting_started/approvals.md | 6 +-- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index a926f4343..5fc2b9e81 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -18,42 +18,42 @@ const ( ) const reportApprovalExample = ` -# Report that a file type artifact has been approved for deployment. -# The approval is for the last 5 git commits -kosli report approval FILE.tgz \ +# Report that an artifact with a provided fingerprint (sha256) has been approved for +# deployment to environment . +# The approval is for all git commits since last approval to this environment. +kosli report approval \ --api-token yourAPIToken \ - --artifact-type file \ --description "An optional description for the approval" \ - --newest-commit $(git rev-parse HEAD) \ - --oldest-commit $(git rev-parse HEAD~5) \ + --environment yourEnvironmentName \ --approver username \ --org yourOrgName \ - --flow yourFlowName + --flow yourFlowName \ + --fingerprint yourArtifactFingerprint -# Report that an artifact with a provided fingerprint (sha256) has been approved for deployment. -# The approval is for the last 5 git commits -kosli report approval \ +# Report that a file type artifact has been approved for deployment to environment . +# The approval is for all git commits since last approval to this environment. +kosli report approval FILE.tgz \ --api-token yourAPIToken \ + --artifact-type file \ --description "An optional description for the approval" \ - --newest-commit $(git rev-parse HEAD) \ - --oldest-commit $(git rev-parse HEAD~5) \ + --environment yourEnvironmentName \ + --newest-commit HEAD \ --approver username \ --org yourOrgName \ - --flow yourFlowName \ - --fingerprint yourArtifactFingerprint + --flow yourFlowName -# Report that an artifact with a provided fingerprint (sha256) has been approved -# for deployment to a specific environment. -# The approval is for all commits from the previous approval of an artifact -# to this environment up to the current commit. +# Report that an artifact with a provided fingerprint (sha256) has been approved for deployment. +# The approval is for all environments. +# The approval is for all commits since the git commit of origin/production branch. kosli report approval \ --api-token yourAPIToken \ --description "An optional description for the approval" \ + --newest-commit HEAD \ + --oldest-commit origin/production \ --approver username \ --org yourOrgName \ --flow yourFlowName \ - --fingerprint yourArtifactFingerprint \ - --environment yourEnvironmentName + --fingerprint yourArtifactFingerprint ` type reportApprovalOptions struct { diff --git a/cmd/kosli/requestApproval.go b/cmd/kosli/requestApproval.go index cfc46ebdd..48fec299f 100644 --- a/cmd/kosli/requestApproval.go +++ b/cmd/kosli/requestApproval.go @@ -14,39 +14,39 @@ The request should be reviewed in the Kosli UI. ) const requestApprovalExample = ` -# Request that a file type artifact needs approval. -# The approval is for the last 5 git commits +# Request an approval for an artifact with a provided fingerprint (sha256) +# for deployment to environment . +# The approval is for all git commits since last approval to this environment. +kosli request approval \ + --api-token yourAPIToken \ + --description "An optional description for the approval" \ + --environment yourEnvironmentName \ + --org yourOrgName \ + --flow yourFlowName \ + --fingerprint yourArtifactFingerprint + +# Request that a file type artifact needs approval for deployment to environment . +# The approval is for all git commits since last approval to this environment. kosli request approval FILE.tgz \ --api-token yourAPIToken \ --artifact-type file \ --description "An optional description for the requested approval" \ - --newest-commit $(git rev-parse HEAD) \ - --oldest-commit $(git rev-parse HEAD~5) \ + --environment yourEnvironmentName \ + --newest-commit HEAD \ --org yourOrgName \ --flow yourFlowName # Request an approval for an artifact with a provided fingerprint (sha256). -# The approval is for the last 5 git commits +# The approval is for all environments. +# The approval is for all commits since the git commit of origin/production branch. kosli request approval \ --api-token yourAPIToken \ --description "An optional description for the requested approval" \ - --newest-commit $(git rev-parse HEAD) \ - --oldest-commit $(git rev-parse HEAD~5) \ + --newest-commit HEAD \ + --oldest-commit origin/production \ --org yourOrgName \ --flow yourFlowName \ --fingerprint yourArtifactFingerprint - -# Request an approval for an artifact with a provided fingerprint (sha256) -# for deployment to a specific environment. -# The approval is for all commits from the previous approval of an artifact -# to this environment up to the current commit. -kosli request approval \ - --api-token yourAPIToken \ - --description "An optional description for the approval" \ - --org yourOrgName \ - --flow yourFlowName \ - --fingerprint yourArtifactFingerprint \ - --environment yourEnvironmentName ` func newRequestApprovalCmd(out io.Writer) *cobra.Command { diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index 1aad0a5f6..a64e12768 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -77,8 +77,8 @@ The service principal needs to have the following permissions: newEnvTypeFlag = "The type of environment. Valid types are: [K8S, ECS, server, S3, lambda, docker]." envAllowListFlag = "The environment name for which the artifact is allowlisted." reasonFlag = "The reason why this artifact is allowlisted." - oldestCommitFlag = "[conditional] The source commit sha for the oldest change in the deployment. Only required if you don't specify '--environment'." - newestCommitFlag = "[defaulted] The source commit sha for the newest change in the deployment." + oldestCommitFlag = "[conditional] The source commit sha for the oldest change in the deployment. Can be any commit-ish. Only required if you don't specify '--environment'." + 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." artifactDescriptionFlag = "[optional] The artifact description." diff --git a/docs.kosli.com/content/getting_started/approvals.md b/docs.kosli.com/content/getting_started/approvals.md index caf4c977a..1382c2317 100644 --- a/docs.kosli.com/content/getting_started/approvals.md +++ b/docs.kosli.com/content/getting_started/approvals.md @@ -7,7 +7,8 @@ weight: 260 Whenever a given artifact is ready to be deployed you may need an additional manual approval from an authorized person. This is something that can't always be automated, but you can use Kosli to request such an approval, and later record it, so the information about decisions made outside of your CI system won't be lost. -The list of commits between current and previous approval will be generated (based on provided values for `--newest-commit` and `--oldest-commit`), which allows you to track a set of changes that are being approved. +The list of commits between current and previous approval will be generated, which allows you to track a set of changes that are being approved. If an approval is for a specific environment the list is based on the previous approval to the same +environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. ## Example @@ -18,8 +19,7 @@ The list of commits between current and previous approval will be generated (bas ``` $ kosli report approval project-a-app.bin \ --artifact-type file \ - --newest-commit $(git rev-parse HEAD) \ - --oldest-commit $(git rev-parse HEAD~1) \ + --environment production \ --flow project-a approval created for artifact: 53c97572093cc107c0caa2906d460ccd65083a4c626f68689e57aafa34b14cbf From e17c3b06478a0bbbddd2a2cb12eea00f725bd27b Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Mon, 20 Nov 2023 13:29:59 +0100 Subject: [PATCH 06/16] Get oldest commit from kosli server if not given in command line --- cmd/kosli/reportApproval.go | 50 +++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 5fc2b9e81..379ea5992 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "fmt" "io" "net/http" @@ -163,9 +164,54 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { if err != nil { return err } - } + o.payload.CommitList, err = o.payloadCommitList() + if err != nil { + return err + } + } else { + // Request last approved git commit from kosli server + url := fmt.Sprintf("%s/api/v2/approvals/%s/%s/last-approved-commit/%s", global.Host, global.Org, + o.flowName, o.payload.Environment) + + getLastApprovedGitCommitParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: url, + DryRun: false, + Password: global.ApiToken, + } + // error and not dry run -> print error message and return err + // error and dry run -> set src_commit_list to o.newestCommit do not send oldestCommit + // no error we get back None -> set src_commit_list to o.newestCommit do not send oldestCommit + // no error we get back a git commit -> call o.payloadCommitList() + + lastApprovedGitCommitResponse, err := kosliClient.Do(getLastApprovedGitCommitParams) + if err != nil { + if !global.DryRun { + return err + } else { + o.payload.CommitList = []string{o.payload.NewestCommit} + } + } else { + var responseData map[string]interface{} + err = json.Unmarshal([]byte(lastApprovedGitCommitResponse.Body), &responseData) + if err != nil { + fmt.Println("unmarshal failed") + return err + } - o.payload.CommitList = []string{} // o.payloadCommitList() + if responseData["commit_sha"] != nil { + o.oldestSrcCommit = responseData["commit_sha"].(string) + o.payload.OldestCommit = o.oldestSrcCommit + o.payload.CommitList, err = o.payloadCommitList() + if err != nil { + return err + } + } else { + o.payload.CommitList = []string{o.payload.NewestCommit} + } + + } + } url := fmt.Sprintf("%s/api/v2/approvals/%s/%s", global.Host, global.Org, o.flowName) From 3650e37370d2a0dad90ac1783bfc40e85d2da437 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 20 Nov 2023 13:53:16 +0100 Subject: [PATCH 07/16] Remove newest_commit key from approval payload --- cmd/kosli/reportApproval.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 379ea5992..38f6e11e2 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -73,7 +73,6 @@ type ApprovalPayload struct { Environment string `json:"environment,omitempty"` Description string `json:"description"` CommitList []string `json:"src_commit_list"` - NewestCommit string `json:"newest_commit"` OldestCommit string `json:"oldest_commit,omitempty"` Reviews []map[string]string `json:"approvals"` UserData interface{} `json:"user_data"` @@ -154,7 +153,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { return err } - o.payload.NewestCommit, err = gitView.ResolveRevision(o.newestSrcCommit) + o.newestSrcCommit, err = gitView.ResolveRevision(o.newestSrcCommit) if err != nil { return err } @@ -189,7 +188,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { if !global.DryRun { return err } else { - o.payload.CommitList = []string{o.payload.NewestCommit} + o.payload.CommitList = []string{o.newestSrcCommit} } } else { var responseData map[string]interface{} @@ -207,7 +206,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { return err } } else { - o.payload.CommitList = []string{o.payload.NewestCommit} + o.payload.CommitList = []string{o.newestSrcCommit} } } From f6468ddb299d6d1951974939747e8ead44ecffbd Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 20 Nov 2023 14:38:48 +0100 Subject: [PATCH 08/16] Add test cases and fix typos in documentation --- cmd/kosli/reportApproval.go | 20 +++++++------------- cmd/kosli/reportApproval_test.go | 26 ++++++++++++++++++++++++++ cmd/kosli/requestApproval.go | 4 ++-- docker-compose.yml | 4 ++-- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 38f6e11e2..5d002db11 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -21,7 +21,7 @@ const ( const reportApprovalExample = ` # Report that an artifact with a provided fingerprint (sha256) has been approved for # deployment to environment . -# The approval is for all git commits since last approval to this environment. +# The approval is for all git commits since the last approval to this environment. kosli report approval \ --api-token yourAPIToken \ --description "An optional description for the approval" \ @@ -32,7 +32,7 @@ kosli report approval \ --fingerprint yourArtifactFingerprint # Report that a file type artifact has been approved for deployment to environment . -# The approval is for all git commits since last approval to this environment. +# The approval is for all git commits since the last approval to this environment. kosli report approval FILE.tgz \ --api-token yourAPIToken \ --artifact-type file \ @@ -120,13 +120,6 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { addFingerprintFlags(cmd, o.fingerprintOptions) addDryRunFlag(cmd) - // err := DeprecateFlags(cmd, map[string]string{ - // "oldest-commit": "use --environment and oldest commit will be calculated", - // }) - // if err != nil { - // logger.Error("failed to configure deprecated flags: %v", err) - // } - err := RequireFlags(cmd, []string{"flow"}) if err != nil { logger.Error("failed to configure required flags: %v", err) @@ -178,16 +171,15 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { DryRun: false, Password: global.ApiToken, } - // error and not dry run -> print error message and return err - // error and dry run -> set src_commit_list to o.newestCommit do not send oldestCommit - // no error we get back None -> set src_commit_list to o.newestCommit do not send oldestCommit - // no error we get back a git commit -> call o.payloadCommitList() lastApprovedGitCommitResponse, err := kosliClient.Do(getLastApprovedGitCommitParams) + if err != nil { if !global.DryRun { + // error and not dry run -> print error message and return err return err } else { + // error and dry run -> set src_commit_list to o.newestCommit do not send oldestCommit o.payload.CommitList = []string{o.newestSrcCommit} } } else { @@ -199,6 +191,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { } if responseData["commit_sha"] != nil { + // no error we get back a git commit -> call o.payloadCommitList() o.oldestSrcCommit = responseData["commit_sha"].(string) o.payload.OldestCommit = o.oldestSrcCommit o.payload.CommitList, err = o.payloadCommitList() @@ -206,6 +199,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { return err } } else { + // no error we get back None -> set src_commit_list to o.newestCommit do not send oldestCommit o.payload.CommitList = []string{o.newestSrcCommit} } diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index 6f831162e..5b33d1d4b 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -12,6 +12,7 @@ type ApprovalReportTestSuite struct { defaultKosliArguments string artifactFingerprint string flowName string + envName string } func (suite *ApprovalReportTestSuite) SetupTest() { @@ -24,9 +25,11 @@ func (suite *ApprovalReportTestSuite) SetupTest() { suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) suite.artifactFingerprint = "847411c6124e719a4e8da2550ac5c116b7ff930493ce8a061486b48db8a5aaa0" suite.flowName = "approval-test" + suite.envName = "staging" CreateFlow(suite.flowName, suite.T()) CreateArtifact(suite.flowName, suite.artifactFingerprint, "foobar", suite.T()) + CreateEnv(global.Org, suite.envName, "K8S", suite.T()) } func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { @@ -37,6 +40,29 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { --newest-commit HEAD --oldest-commit HEAD~3` + suite.defaultKosliArguments, golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), }, + { + name: "report approval with an environment name works", + cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. + --newest-commit HEAD --oldest-commit HEAD~3` + ` --environment staging` + suite.defaultKosliArguments, + golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), + }, + { + name: "report approval with an environment name and no oldest-commit and no newest-commit works", + cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + + ` --environment ` + suite.envName + suite.defaultKosliArguments, + golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), + }, + { + wantError: true, + name: "report approval with no environment name or oldest commit fails", + cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + + suite.defaultKosliArguments, + golden: "Error: at least one of --environment, --oldest-commit is required\n", + }, + // Here is a case we need to investigate how to test: + // - Create approval with '--newest-commit HEAD~5', '--oldest-commit HEAD~7' and '--environment staging', + // then create approval only with '--environment staging', + // the resulting payload should contain a commit list of 4 commits, and an oldest_commit of HEAD~5 } runTestCmd(suite.T(), tests) } diff --git a/cmd/kosli/requestApproval.go b/cmd/kosli/requestApproval.go index 48fec299f..33bf372ed 100644 --- a/cmd/kosli/requestApproval.go +++ b/cmd/kosli/requestApproval.go @@ -16,7 +16,7 @@ The request should be reviewed in the Kosli UI. const requestApprovalExample = ` # Request an approval for an artifact with a provided fingerprint (sha256) # for deployment to environment . -# The approval is for all git commits since last approval to this environment. +# The approval is for all git commits since the last approval to this environment. kosli request approval \ --api-token yourAPIToken \ --description "An optional description for the approval" \ @@ -26,7 +26,7 @@ kosli request approval \ --fingerprint yourArtifactFingerprint # Request that a file type artifact needs approval for deployment to environment . -# The approval is for all git commits since last approval to this environment. +# The approval is for all git commits since the last approval to this environment. kosli request approval FILE.tgz \ --api-token yourAPIToken \ --artifact-type file \ diff --git a/docker-compose.yml b/docker-compose.yml index 8a76f799c..42e183426 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: server-index: networks: [ cli_net ] depends_on: [ mongo_rs_initiate, minio ] - image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:latest + image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:0d3e1c6 command: /app/src/documentdb/wait_till_ready_or_raise.py container_name: cli_kosli_server-index read_only: true @@ -32,7 +32,7 @@ services: condition: service_started server-index: condition: service_completed_successfully - image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:latest + image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:0d3e1c6 env_file: [ "./mongo/mongo.env" ] environment: KOSLI_HOSTNAME: localhost From 388783bdf80ef52444740007cf0a100596612feb Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Mon, 27 Nov 2023 10:52:55 +0100 Subject: [PATCH 09/16] Approval: Updated URL. Started on some more tests --- cmd/kosli/reportApproval.go | 2 +- cmd/kosli/reportApproval_test.go | 23 ++++++++++++++++------- cmd/kosli/testHelpers.go | 21 +++++++++++++++++++++ docker-compose.yml | 4 ++-- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 5d002db11..d6f77df12 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -162,7 +162,7 @@ func (o *reportApprovalOptions) run(args []string, request bool) error { } } else { // Request last approved git commit from kosli server - url := fmt.Sprintf("%s/api/v2/approvals/%s/%s/last-approved-commit/%s", global.Host, global.Org, + url := fmt.Sprintf("%s/api/v2/approvals/%s/%s/artifact-commit/%s", global.Host, global.Org, o.flowName, o.payload.Environment) getLastApprovedGitCommitParams := &requests.RequestParams{ diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index 5b33d1d4b..d0c0e8a95 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -13,6 +13,7 @@ type ApprovalReportTestSuite struct { artifactFingerprint string flowName string envName string + gitCommit string } func (suite *ApprovalReportTestSuite) SetupTest() { @@ -26,10 +27,12 @@ func (suite *ApprovalReportTestSuite) SetupTest() { suite.artifactFingerprint = "847411c6124e719a4e8da2550ac5c116b7ff930493ce8a061486b48db8a5aaa0" suite.flowName = "approval-test" suite.envName = "staging" + suite.gitCommit = "993a9a6be532ed4e7a87aab4df90a7f1b3168d63" CreateFlow(suite.flowName, suite.T()) - CreateArtifact(suite.flowName, suite.artifactFingerprint, "foobar", suite.T()) + CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, "foobar", suite.gitCommit, suite.T()) CreateEnv(global.Org, suite.envName, "K8S", suite.T()) + } func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { @@ -46,12 +49,6 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { --newest-commit HEAD --oldest-commit HEAD~3` + ` --environment staging` + suite.defaultKosliArguments, golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), }, - { - name: "report approval with an environment name and no oldest-commit and no newest-commit works", - cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + - ` --environment ` + suite.envName + suite.defaultKosliArguments, - golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), - }, { wantError: true, name: "report approval with no environment name or oldest commit fails", @@ -59,6 +56,18 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { suite.defaultKosliArguments, golden: "Error: at least one of --environment, --oldest-commit is required\n", }, + // For the next test we have to + // - (create a flow) + // - create an artifact in that flow with git commit HEAD~5 + // - (create an environment) + // - create snapshot that contains this artifact + // { + // name: "report approval with an environment name and no oldest-commit and no newest-commit works", + // cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + + // ` --environment ` + suite.envName + suite.defaultKosliArguments, + // golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), + // }, + // Here is a case we need to investigate how to test: // - Create approval with '--newest-commit HEAD~5', '--oldest-commit HEAD~7' and '--environment staging', // then create approval only with '--environment staging', diff --git a/cmd/kosli/testHelpers.go b/cmd/kosli/testHelpers.go index fbecb6b34..c7d36d398 100644 --- a/cmd/kosli/testHelpers.go +++ b/cmd/kosli/testHelpers.go @@ -203,6 +203,27 @@ func CreateArtifact(flowName, artifactFingerprint, artifactName string, t *testi require.NoError(t, err, "artifact should be created without error") } +func CreateArtifactWithCommit(flowName, artifactFingerprint, artifactName string, gitCommit string, t *testing.T) { + t.Helper() + o := &reportArtifactOptions{ + srcRepoRoot: "../..", + flowName: flowName, + //name: "", + gitReference: gitCommit, + payload: ArtifactPayload{ + Fingerprint: artifactFingerprint, + GitCommit: gitCommit, + BuildUrl: "www.yr.no", + CommitUrl: "www.nrk.no", + }, + } + + o.fingerprintOptions = new(fingerprintOptions) + + err := o.run([]string{artifactName}) + require.NoError(t, err, "artifact should be created without error") +} + // CreateApproval creates an approval for an artifact in a flow func CreateApproval(flowName, fingerprint string, t *testing.T) { t.Helper() diff --git a/docker-compose.yml b/docker-compose.yml index 42e183426..76619b539 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: server-index: networks: [ cli_net ] depends_on: [ mongo_rs_initiate, minio ] - image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:0d3e1c6 + image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:c8efd7a command: /app/src/documentdb/wait_till_ready_or_raise.py container_name: cli_kosli_server-index read_only: true @@ -32,7 +32,7 @@ services: condition: service_started server-index: condition: service_completed_successfully - image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:0d3e1c6 + image: 772819027869.dkr.ecr.eu-central-1.amazonaws.com/merkely:c8efd7a env_file: [ "./mongo/mongo.env" ] environment: KOSLI_HOSTNAME: localhost From 40df48037cb879c8853c5e7511dbbd7a44fae411 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 27 Nov 2023 12:03:29 +0100 Subject: [PATCH 10/16] Create setup to make a snapshot in approval tests --- cmd/kosli/reportApproval_test.go | 39 ++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index d0c0e8a95..0448b90ad 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -14,6 +14,11 @@ type ApprovalReportTestSuite struct { flowName string envName string gitCommit string + artifactPath string +} + +type reportApprovalTestConfig struct { + createSnapshot bool } func (suite *ApprovalReportTestSuite) SetupTest() { @@ -24,15 +29,16 @@ func (suite *ApprovalReportTestSuite) SetupTest() { } suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) - suite.artifactFingerprint = "847411c6124e719a4e8da2550ac5c116b7ff930493ce8a061486b48db8a5aaa0" suite.flowName = "approval-test" suite.envName = "staging" suite.gitCommit = "993a9a6be532ed4e7a87aab4df90a7f1b3168d63" + suite.artifactPath = "testdata/report.xml" + suite.artifactFingerprint = "c6b02fb1708fbc46e63f5074c38d17852bfa0c7bcfcdd1f877e80476e3fcb74f" + // _, suite.artifactFingerprint, _ = executeCommandC(fmt.Sprintf("kosli fingerprint" + suite.artifactPath + "--artifact-type file" + suite.defaultKosliArguments)) CreateFlow(suite.flowName, suite.T()) - CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, "foobar", suite.gitCommit, suite.T()) - CreateEnv(global.Org, suite.envName, "K8S", suite.T()) - + CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, suite.artifactPath, suite.gitCommit, suite.T()) + CreateEnv(global.Org, suite.envName, "server", suite.T()) } func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { @@ -57,23 +63,32 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { golden: "Error: at least one of --environment, --oldest-commit is required\n", }, // For the next test we have to - // - (create a flow) + // - create a flow // - create an artifact in that flow with git commit HEAD~5 // - (create an environment) // - create snapshot that contains this artifact - // { - // name: "report approval with an environment name and no oldest-commit and no newest-commit works", - // cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + - // ` --environment ` + suite.envName + suite.defaultKosliArguments, - // golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), - // }, + { + name: "report approval with an environment name and no oldest-commit and no newest-commit works", + cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + + ` --environment ` + suite.envName + suite.defaultKosliArguments, + golden: fmt.Sprintf("approval created for artifact: %s\n", suite.artifactFingerprint), + additionalConfig: reportApprovalTestConfig{ + createSnapshot: true, + }, + }, // Here is a case we need to investigate how to test: // - Create approval with '--newest-commit HEAD~5', '--oldest-commit HEAD~7' and '--environment staging', // then create approval only with '--environment staging', // the resulting payload should contain a commit list of 4 commits, and an oldest_commit of HEAD~5 } - runTestCmd(suite.T(), tests) + for _, t := range tests { + if t.additionalConfig != nil && t.additionalConfig.(reportApprovalTestConfig).createSnapshot { + ReportServerArtifactToEnv([]string{suite.artifactPath}, suite.envName, suite.T()) + } + runTestCmd(suite.T(), []cmdTestCase{t}) + } + // runTestCmd(suite.T(), tests) } func TestApprovalReportCommandTestSuite(t *testing.T) { From f661515c029b076334041f5655781c58c42a68c7 Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Mon, 27 Nov 2023 14:43:01 +0100 Subject: [PATCH 11/16] Approval: calculate HEAD~5 in kosli approval command --- cmd/kosli/reportApproval_test.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index 0448b90ad..c43baa6e7 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + "github.com/kosli-dev/cli/internal/gitview" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -31,14 +33,25 @@ func (suite *ApprovalReportTestSuite) SetupTest() { suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) suite.flowName = "approval-test" suite.envName = "staging" - suite.gitCommit = "993a9a6be532ed4e7a87aab4df90a7f1b3168d63" + t := suite.T() + + gitView, err := gitview.New("../..") + require.NoError(t, err, "Failed to create gitview") + + suite.gitCommit, err = gitView.ResolveRevision("HEAD~5") + require.NoError(t, err, "Failed to get HEAD~5") + suite.artifactPath = "testdata/report.xml" suite.artifactFingerprint = "c6b02fb1708fbc46e63f5074c38d17852bfa0c7bcfcdd1f877e80476e3fcb74f" - // _, suite.artifactFingerprint, _ = executeCommandC(fmt.Sprintf("kosli fingerprint" + suite.artifactPath + "--artifact-type file" + suite.defaultKosliArguments)) - CreateFlow(suite.flowName, suite.T()) - CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, suite.artifactPath, suite.gitCommit, suite.T()) - CreateEnv(global.Org, suite.envName, "server", suite.T()) + // cmd := fmt.Sprintf("fingerprint %s --artifact-type file", suite.artifactPath) + // _, output, err := executeCommandC(cmd) + // require.NoError(t, err, "Failed to calculate fingerprint") + // suite.artifactFingerprint = strings.Trim(suite.artifactFingerprint, "\n") + + CreateFlow(suite.flowName, t) + CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, suite.artifactPath, suite.gitCommit, t) + CreateEnv(global.Org, suite.envName, "server", t) } func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { @@ -62,11 +75,6 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { suite.defaultKosliArguments, golden: "Error: at least one of --environment, --oldest-commit is required\n", }, - // For the next test we have to - // - create a flow - // - create an artifact in that flow with git commit HEAD~5 - // - (create an environment) - // - create snapshot that contains this artifact { name: "report approval with an environment name and no oldest-commit and no newest-commit works", cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + From f52e85eff1012bd30120ce88a81490306957ec7e Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 27 Nov 2023 14:51:06 +0100 Subject: [PATCH 12/16] Tests: calculate fingerprint of artifact for approval using function instead of kosli command --- cmd/kosli/reportApproval_test.go | 11 +++++------ cmd/kosli/testHelpers.go | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index c43baa6e7..8b51c8b60 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/kosli-dev/cli/internal/digest" "github.com/kosli-dev/cli/internal/gitview" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -42,12 +43,10 @@ func (suite *ApprovalReportTestSuite) SetupTest() { require.NoError(t, err, "Failed to get HEAD~5") suite.artifactPath = "testdata/report.xml" - suite.artifactFingerprint = "c6b02fb1708fbc46e63f5074c38d17852bfa0c7bcfcdd1f877e80476e3fcb74f" - - // cmd := fmt.Sprintf("fingerprint %s --artifact-type file", suite.artifactPath) - // _, output, err := executeCommandC(cmd) - // require.NoError(t, err, "Failed to calculate fingerprint") - // suite.artifactFingerprint = strings.Trim(suite.artifactFingerprint, "\n") + // We cannot get the digest of the file by running the 'kosli fingerprint' command + // by using executeCommandC() because this function overwrites the global options + suite.artifactFingerprint, err = digest.FileSha256(suite.artifactPath) + require.NoError(t, err, "Failed to calculate fingerprint") CreateFlow(suite.flowName, t) CreateArtifactWithCommit(suite.flowName, suite.artifactFingerprint, suite.artifactPath, suite.gitCommit, t) diff --git a/cmd/kosli/testHelpers.go b/cmd/kosli/testHelpers.go index c7d36d398..6c82f78dc 100644 --- a/cmd/kosli/testHelpers.go +++ b/cmd/kosli/testHelpers.go @@ -25,6 +25,8 @@ type cmdTestCase struct { } // executeCommandStdinC executes a command as a user would and return the output +// this creates a new kosli command that is run, but it cannot be used in other tests +// because newRootCmd overwrites the global options func executeCommandC(cmd string) (*cobra.Command, string, error) { args, err := shellwords.Parse(cmd) if err != nil { From a495036d067836b99635eadc97e2b6fc41a6ab49 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 27 Nov 2023 15:06:42 +0100 Subject: [PATCH 13/16] Delete dead comments --- cmd/kosli/reportApproval_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cmd/kosli/reportApproval_test.go b/cmd/kosli/reportApproval_test.go index 8b51c8b60..d0a65ac62 100644 --- a/cmd/kosli/reportApproval_test.go +++ b/cmd/kosli/reportApproval_test.go @@ -69,7 +69,7 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { }, { wantError: true, - name: "report approval with no environment name or oldest commit fails", + name: "report approval with no environment name and no oldest commit fails", cmd: `report approval --fingerprint ` + suite.artifactFingerprint + ` --flow ` + suite.flowName + ` --repo-root ../.. ` + suite.defaultKosliArguments, golden: "Error: at least one of --environment, --oldest-commit is required\n", @@ -83,11 +83,6 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { createSnapshot: true, }, }, - - // Here is a case we need to investigate how to test: - // - Create approval with '--newest-commit HEAD~5', '--oldest-commit HEAD~7' and '--environment staging', - // then create approval only with '--environment staging', - // the resulting payload should contain a commit list of 4 commits, and an oldest_commit of HEAD~5 } for _, t := range tests { if t.additionalConfig != nil && t.additionalConfig.(reportApprovalTestConfig).createSnapshot { @@ -95,7 +90,6 @@ func (suite *ApprovalReportTestSuite) TestApprovalReportCmd() { } runTestCmd(suite.T(), []cmdTestCase{t}) } - // runTestCmd(suite.T(), tests) } func TestApprovalReportCommandTestSuite(t *testing.T) { From e7c2c6bb13ad114e1f9c1ce3058bf43c0847e9e6 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Tue, 28 Nov 2023 10:20:06 +0100 Subject: [PATCH 14/16] Update docs for approvals --- charts/k8s-reporter/README.md | 4 +- cmd/kosli/reportApproval.go | 2 +- cmd/kosli/requestApproval.go | 9 +- cmd/kosli/root.go | 213 +++++++++--------- .../content/getting_started/approvals.md | 8 +- docs.kosli.com/content/helm/_index.md | 4 +- 6 files changed, 116 insertions(+), 124 deletions(-) diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index a67e3c0a6..579937da7 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -61,7 +61,7 @@ helm upgrade [RELEASE-NAME] kosli/k8s-reporter | kosliApiToken.secretKey | string | `""` | the name of the key in the secret data which contains the kosli API token | | kosliApiToken.secretName | string | `""` | the name of the secret containing the kosli API token | | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | -| podAnnotations | object | `{}` | | +| podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | | reporterConfig.kosliEnvironmentName | string | `""` | the name of kosli environment that the k8s cluster/namespace correlates to | | reporterConfig.kosliOrg | string | `""` | the name of the kosli org | @@ -74,5 +74,5 @@ helm upgrade [RELEASE-NAME] kosli/k8s-reporter | serviceAccount.name | string | `""` | the name of the service account to use. If not set and create is true, a name is generated using the fullname template | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index d6f77df12..57bc6522f 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -109,7 +109,7 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&o.payload.ArtifactFingerprint, "fingerprint", "F", "", fingerprintFlag) - cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", environmentNameFlag) + cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", approvalEnvironmentNameFlag) cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) diff --git a/cmd/kosli/requestApproval.go b/cmd/kosli/requestApproval.go index 33bf372ed..a71c7f44a 100644 --- a/cmd/kosli/requestApproval.go +++ b/cmd/kosli/requestApproval.go @@ -80,7 +80,7 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command { } cmd.Flags().StringVarP(&o.payload.ArtifactFingerprint, "fingerprint", "F", "", fingerprintFlag) - cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", environmentNameFlag) + cmd.Flags().StringVarP(&o.payload.Environment, "environment", "e", "", approvalEnvironmentNameFlag) cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag) cmd.Flags().StringVarP(&o.payload.Description, "description", "d", "", approvalDescriptionFlag) cmd.Flags().StringVarP(&o.userDataFile, "user-data", "u", "", approvalUserDataFlag) @@ -90,13 +90,6 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command { addFingerprintFlags(cmd, o.fingerprintOptions) addDryRunFlag(cmd) - // err := DeprecateFlags(cmd, map[string]string{ - // "oldest-commit": "use --environment and oldest commit will be calculated", - // }) - // if err != nil { - // logger.Error("failed to configure deprecated flags: %v", err) - // } - err := RequireFlags(cmd, []string{"flow"}) if err != nil { logger.Error("failed to configure required flags: %v", err) diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index a64e12768..280fd54db 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -52,112 +52,113 @@ The service principal needs to have the following permissions: ` // flags - apiTokenFlag = "The Kosli API token." - artifactName = "[optional] Artifact display name, if different from file, image or directory name." - orgFlag = "The Kosli organization." - hostFlag = "[defaulted] The Kosli endpoint." - dryRunFlag = "[optional] Run in dry-run mode. When enabled, no data is sent to Kosli and the CLI exits with 0 exit code regardless of any errors." - maxAPIRetryFlag = "[defaulted] How many times should API calls be retried when the API host is not reachable." - configFileFlag = "[optional] The Kosli config file path." - verboseFlag = "[optional] Print verbose logs to stdout." - debugFlag = "[optional] Print debug logs to stdout. A boolean flag https://docs.kosli.com/faq/#boolean-flags (default false)" - artifactTypeFlag = "[conditional] The type of the artifact to calculate its SHA256 fingerprint. One of: [docker, file, dir]. Only required if you don't specify '--fingerprint'." - flowNameFlag = "The Kosli flow name." - auditTrailNameFlag = "The Kosli audit trail name." - workflowIDFlag = "The ID of the workflow." - stepNameFlag = "The name of the step as defined in the audit trail's steps." - flowNamesFlag = "[defaulted] The comma separated list of Kosli flows. Defaults to all flows of the org." - newFlowFlag = "The name of the flow to be created or updated." - outputFlag = "[defaulted] The format of the output. Valid formats are: [table, json]." - pipefileFlag = "[deprecated] The path to the JSON pipefile." - environmentNameFlag = "The environment name." - pageNumberFlag = "[defaulted] The page number of a response." - pageLimitFlag = "[defaulted] The number of elements per page." - newEnvNameFlag = "The name of environment to be created." - newEnvTypeFlag = "The type of environment. Valid types are: [K8S, ECS, server, S3, lambda, docker]." - envAllowListFlag = "The environment name for which the artifact is allowlisted." - reasonFlag = "The reason why this artifact is allowlisted." - oldestCommitFlag = "[conditional] The source commit sha for the oldest change in the deployment. Can be any commit-ish. Only required if you don't specify '--environment'." - 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." - artifactDescriptionFlag = "[optional] The artifact description." - deploymentDescriptionFlag = "[optional] The deployment description." - evidenceDescriptionFlag = "[optional] The evidence description." - jiraBaseUrlFlag = "The base url for the jira project, e.g. 'https://kosli.atlassian.net/browse/'" - jiraUsernameFlag = "Jira username (for Jira Cloud)" - jiraAPITokenFlag = "Jira API token (for Jira Cloud)" - jiraPATFlag = "Jira personal access token (for self-hosted Jira)" - envDescriptionFlag = "[optional] The environment description." - flowDescriptionFlag = "[optional] The Kosli flow description." - workflowDescriptionFlag = "[optional] The Kosli Workflow description." - visibilityFlag = "[defaulted] The visibility of the Kosli flow. Valid visibilities are [public, private]." - templateFlag = "[defaulted] The comma-separated list of required compliance controls names." - stepsFlag = "[defaulted] The comma-separated list of required audit trail steps names." - approvalUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this approval." - evidenceUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this evidence." - deploymentUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this deployment." - gitCommitFlag = "The git commit from which the artifact was created. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - evidenceBuildUrlFlag = "The url of CI pipeline that generated the evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - buildUrlFlag = "The url of CI pipeline that built the artifact. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - commitUrlFlag = "The url for the git commit that created the artifact. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - evidenceCompliantFlag = "[defaulted] Whether the evidence is compliant or not. A boolean flag https://docs.kosli.com/faq/#boolean-flags" - evidenceTypeFlag = "The type of evidence being reported." - bbUsernameFlag = "Bitbucket username." - bbPasswordFlag = "Bitbucket App password. See https://developer.atlassian.com/cloud/bitbucket/rest/intro/#authentication for more details." - bbWorkspaceFlag = "Bitbucket workspace ID." - commitPREvidenceFlag = "Git commit for which to find pull request evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - commitEvidenceFlag = "Git commit for which to verify and given evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - repositoryFlag = "Git repository. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - assertPREvidenceFlag = "[optional] Exit with non-zero code if no pull requests found for the given commit." - assertJiraEvidenceFlag = "[optional] Exit with non-zero code if no jira issue reference found, or jira issue does not exist, for the given commit or branch." - assertStatusFlag = "[optional] Exit with non-zero code if Kosli server is not responding." - azureTokenFlag = "Azure Personal Access token." - azureProjectFlag = "Azure project.(defaulted if you are running in Azure Devops pipelines: https://docs.kosli.com/ci-defaults )." - azureOrgUrlFlag = "Azure organization url. E.g. \"https://dev.azure.com/myOrg\" (defaulted if you are running in Azure Devops pipelines: https://docs.kosli.com/ci-defaults )." - azureBaseURLFlag = "[optional] Azure Devops base URL." - azureClientIdFlag = "Azure client ID." - azureClientSecretFlag = "Azure client secret." - azureTenantIdFlag = "Azure tenant ID." - azureSubscriptionIdFlag = "Azure subscription ID." - azureResourceGroupNameFlag = "Azure resource group name." - githubTokenFlag = "Github token." - githubOrgFlag = "Github organization. (defaulted if you are running in GitHub Actions: https://docs.kosli.com/ci-defaults )." - githubBaseURLFlag = "[optional] GitHub base URL (only needed for GitHub Enterprise installations)." - gitlabTokenFlag = "Gitlab token." - gitlabOrgFlag = "Gitlab organization. (defaulted if you are running in Gitlab Pipelines: https://docs.kosli.com/ci-defaults )." - gitlabBaseURLFlag = "[optional] Gitlab base URL (only needed for on-prem Gitlab installations)." - registryProviderFlag = "[conditional] The docker registry provider or url. Only required if you want to read docker image SHA256 digest from a remote docker registry." - registryUsernameFlag = "[conditional] The docker registry username. Only required if you want to read docker image SHA256 digest from a remote docker registry." - registryPasswordFlag = "[conditional] The docker registry password or access token. Only required if you want to read docker image SHA256 digest from a remote docker registry." - resultsDirFlag = "[defaulted] The path to a directory with JUnit test results. The directory will be uploaded to Kosli's evidence vault." - snykJsonResultsFileFlag = "The path to Snyk scan results JSON file from 'snyk test' and 'snyk container test'. The Snyk results will be uploaded to Kosli's evidence vault." - ecsClusterFlag = "The name of the ECS cluster." - ecsServiceFlag = "[optional] The name of the ECS service." - kubeconfigFlag = "[defaulted] The kubeconfig path for the target cluster." - namespaceFlag = "[conditional] The comma separated list of namespaces regex patterns to report artifacts info from. Can't be used together with --exclude-namespace." - excludeNamespaceFlag = "[conditional] The comma separated list of namespaces regex patterns NOT to report artifacts info from. Can't be used together with --namespace." - functionNameFlag = "[optional] The name of the AWS Lambda function." - functionNamesFlag = "[optional] The comma-separated list of AWS Lambda function names to be reported." - functionVersionFlag = "[optional] The version of the AWS Lambda function." - awsKeyIdFlag = "The AWS access key ID." - awsSecretKeyFlag = "The AWS secret access key." - awsRegionFlag = "The AWS region." - bucketNameFlag = "The name of the S3 bucket." - pathsFlag = "The comma separated list of artifact directories." - excludePathsFlag = "[optional] The comma separated list of directories and files to exclude from fingerprinting. Only applicable for --artifact-type dir." - shortFlag = "[optional] Print only the Kosli CLI version number." - longFlag = "[optional] Print detailed output." - reverseFlag = "[defaulted] Reverse the order of output list." - evidenceNameFlag = "The name of the evidence." - evidenceFingerprintFlag = "[optional] The SHA256 fingerprint of the evidence file or dir." - evidenceURLFlag = "[optional] The external URL where the evidence file or dir is stored." - evidencePathsFlag = "[optional] The comma-separated list of paths containing supporting proof for the reported evidence. Paths can be for files or directories. All provided proofs will be uploaded to Kosli's evidence vault." - fingerprintFlag = "[conditional] The SHA256 fingerprint of the artifact. Only required if you don't specify '--artifact-type'." - evidenceCommitFlag = "The git commit SHA1 for which the evidence belongs. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." - intervalFlag = "[optional] Expression to define specified snapshots range." - showUnchangedArtifactsFlag = "[defaulted] Show the unchanged artifacts present in both snapshots within the diff output." - approverFlag = "[optional] The user approving an approval." + apiTokenFlag = "The Kosli API token." + artifactName = "[optional] Artifact display name, if different from file, image or directory name." + orgFlag = "The Kosli organization." + hostFlag = "[defaulted] The Kosli endpoint." + dryRunFlag = "[optional] Run in dry-run mode. When enabled, no data is sent to Kosli and the CLI exits with 0 exit code regardless of any errors." + maxAPIRetryFlag = "[defaulted] How many times should API calls be retried when the API host is not reachable." + configFileFlag = "[optional] The Kosli config file path." + verboseFlag = "[optional] Print verbose logs to stdout." + debugFlag = "[optional] Print debug logs to stdout. A boolean flag https://docs.kosli.com/faq/#boolean-flags (default false)" + artifactTypeFlag = "[conditional] The type of the artifact to calculate its SHA256 fingerprint. One of: [docker, file, dir]. Only required if you don't specify '--fingerprint'." + flowNameFlag = "The Kosli flow name." + auditTrailNameFlag = "The Kosli audit trail name." + workflowIDFlag = "The ID of the workflow." + stepNameFlag = "The name of the step as defined in the audit trail's steps." + flowNamesFlag = "[defaulted] The comma separated list of Kosli flows. Defaults to all flows of the org." + newFlowFlag = "The name of the flow to be created or updated." + outputFlag = "[defaulted] The format of the output. Valid formats are: [table, json]." + pipefileFlag = "[deprecated] The path to the JSON pipefile." + environmentNameFlag = "The environment name." + approvalEnvironmentNameFlag = "[defaulted] The environment the artifact is approved for. (defaults to all environments)" + pageNumberFlag = "[defaulted] The page number of a response." + pageLimitFlag = "[defaulted] The number of elements per page." + newEnvNameFlag = "The name of environment to be created." + newEnvTypeFlag = "The type of environment. Valid types are: [K8S, ECS, server, S3, lambda, docker]." + envAllowListFlag = "The environment name for which the artifact is allowlisted." + reasonFlag = "The reason why this artifact is allowlisted." + oldestCommitFlag = "[conditional] The source commit sha for the oldest change in the deployment. Can be any commit-ish. Only required if you don't specify '--environment'." + 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." + artifactDescriptionFlag = "[optional] The artifact description." + deploymentDescriptionFlag = "[optional] The deployment description." + evidenceDescriptionFlag = "[optional] The evidence description." + jiraBaseUrlFlag = "The base url for the jira project, e.g. 'https://kosli.atlassian.net/browse/'" + jiraUsernameFlag = "Jira username (for Jira Cloud)" + jiraAPITokenFlag = "Jira API token (for Jira Cloud)" + jiraPATFlag = "Jira personal access token (for self-hosted Jira)" + envDescriptionFlag = "[optional] The environment description." + flowDescriptionFlag = "[optional] The Kosli flow description." + workflowDescriptionFlag = "[optional] The Kosli Workflow description." + visibilityFlag = "[defaulted] The visibility of the Kosli flow. Valid visibilities are [public, private]." + templateFlag = "[defaulted] The comma-separated list of required compliance controls names." + stepsFlag = "[defaulted] The comma-separated list of required audit trail steps names." + approvalUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this approval." + evidenceUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this evidence." + deploymentUserDataFlag = "[optional] The path to a JSON file containing additional data you would like to attach to this deployment." + gitCommitFlag = "The git commit from which the artifact was created. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + evidenceBuildUrlFlag = "The url of CI pipeline that generated the evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + buildUrlFlag = "The url of CI pipeline that built the artifact. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + commitUrlFlag = "The url for the git commit that created the artifact. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + evidenceCompliantFlag = "[defaulted] Whether the evidence is compliant or not. A boolean flag https://docs.kosli.com/faq/#boolean-flags" + evidenceTypeFlag = "The type of evidence being reported." + bbUsernameFlag = "Bitbucket username." + bbPasswordFlag = "Bitbucket App password. See https://developer.atlassian.com/cloud/bitbucket/rest/intro/#authentication for more details." + bbWorkspaceFlag = "Bitbucket workspace ID." + commitPREvidenceFlag = "Git commit for which to find pull request evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + commitEvidenceFlag = "Git commit for which to verify and given evidence. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + repositoryFlag = "Git repository. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + assertPREvidenceFlag = "[optional] Exit with non-zero code if no pull requests found for the given commit." + assertJiraEvidenceFlag = "[optional] Exit with non-zero code if no jira issue reference found, or jira issue does not exist, for the given commit or branch." + assertStatusFlag = "[optional] Exit with non-zero code if Kosli server is not responding." + azureTokenFlag = "Azure Personal Access token." + azureProjectFlag = "Azure project.(defaulted if you are running in Azure Devops pipelines: https://docs.kosli.com/ci-defaults )." + azureOrgUrlFlag = "Azure organization url. E.g. \"https://dev.azure.com/myOrg\" (defaulted if you are running in Azure Devops pipelines: https://docs.kosli.com/ci-defaults )." + azureBaseURLFlag = "[optional] Azure Devops base URL." + azureClientIdFlag = "Azure client ID." + azureClientSecretFlag = "Azure client secret." + azureTenantIdFlag = "Azure tenant ID." + azureSubscriptionIdFlag = "Azure subscription ID." + azureResourceGroupNameFlag = "Azure resource group name." + githubTokenFlag = "Github token." + githubOrgFlag = "Github organization. (defaulted if you are running in GitHub Actions: https://docs.kosli.com/ci-defaults )." + githubBaseURLFlag = "[optional] GitHub base URL (only needed for GitHub Enterprise installations)." + gitlabTokenFlag = "Gitlab token." + gitlabOrgFlag = "Gitlab organization. (defaulted if you are running in Gitlab Pipelines: https://docs.kosli.com/ci-defaults )." + gitlabBaseURLFlag = "[optional] Gitlab base URL (only needed for on-prem Gitlab installations)." + registryProviderFlag = "[conditional] The docker registry provider or url. Only required if you want to read docker image SHA256 digest from a remote docker registry." + registryUsernameFlag = "[conditional] The docker registry username. Only required if you want to read docker image SHA256 digest from a remote docker registry." + registryPasswordFlag = "[conditional] The docker registry password or access token. Only required if you want to read docker image SHA256 digest from a remote docker registry." + resultsDirFlag = "[defaulted] The path to a directory with JUnit test results. The directory will be uploaded to Kosli's evidence vault." + snykJsonResultsFileFlag = "The path to Snyk scan results JSON file from 'snyk test' and 'snyk container test'. The Snyk results will be uploaded to Kosli's evidence vault." + ecsClusterFlag = "The name of the ECS cluster." + ecsServiceFlag = "[optional] The name of the ECS service." + kubeconfigFlag = "[defaulted] The kubeconfig path for the target cluster." + namespaceFlag = "[conditional] The comma separated list of namespaces regex patterns to report artifacts info from. Can't be used together with --exclude-namespace." + excludeNamespaceFlag = "[conditional] The comma separated list of namespaces regex patterns NOT to report artifacts info from. Can't be used together with --namespace." + functionNameFlag = "[optional] The name of the AWS Lambda function." + functionNamesFlag = "[optional] The comma-separated list of AWS Lambda function names to be reported." + functionVersionFlag = "[optional] The version of the AWS Lambda function." + awsKeyIdFlag = "The AWS access key ID." + awsSecretKeyFlag = "The AWS secret access key." + awsRegionFlag = "The AWS region." + bucketNameFlag = "The name of the S3 bucket." + pathsFlag = "The comma separated list of artifact directories." + excludePathsFlag = "[optional] The comma separated list of directories and files to exclude from fingerprinting. Only applicable for --artifact-type dir." + shortFlag = "[optional] Print only the Kosli CLI version number." + longFlag = "[optional] Print detailed output." + reverseFlag = "[defaulted] Reverse the order of output list." + evidenceNameFlag = "The name of the evidence." + evidenceFingerprintFlag = "[optional] The SHA256 fingerprint of the evidence file or dir." + evidenceURLFlag = "[optional] The external URL where the evidence file or dir is stored." + evidencePathsFlag = "[optional] The comma-separated list of paths containing supporting proof for the reported evidence. Paths can be for files or directories. All provided proofs will be uploaded to Kosli's evidence vault." + fingerprintFlag = "[conditional] The SHA256 fingerprint of the artifact. Only required if you don't specify '--artifact-type'." + evidenceCommitFlag = "The git commit SHA1 for which the evidence belongs. (defaulted in some CIs: https://docs.kosli.com/ci-defaults )." + intervalFlag = "[optional] Expression to define specified snapshots range." + showUnchangedArtifactsFlag = "[defaulted] Show the unchanged artifacts present in both snapshots within the diff output." + approverFlag = "[optional] The user approving an approval." ) var global *GlobalOpts diff --git a/docs.kosli.com/content/getting_started/approvals.md b/docs.kosli.com/content/getting_started/approvals.md index 1382c2317..78d3370c7 100644 --- a/docs.kosli.com/content/getting_started/approvals.md +++ b/docs.kosli.com/content/getting_started/approvals.md @@ -5,14 +5,12 @@ weight: 260 --- # Part 7: Approvals -Whenever a given artifact is ready to be deployed you may need an additional manual approval from an authorized person. This is something that can't always be automated, but you can use Kosli to request such an approval, and later record it, so the information about decisions made outside of your CI system won't be lost. +Whenever a given artifact is ready to be deployed you may need an additional approval. In Kosli, an Approval means an artifact is ready to be deployed to a given [environment](/getting_started/environments/). An approval can be manually or automatically, and recorded into Kosli so the decision made outside of your CI system won't be lost. -The list of commits between current and previous approval will be generated, which allows you to track a set of changes that are being approved. If an approval is for a specific environment the list is based on the previous approval to the same -environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. +When an Approval is created for an artifact to a specific environment with the `--environment` flag, Kosli will generate a list of commits to be approved. By default, this list will contain all commits between `HEAD` and the commit of the latest artifact coming from the same [flow](/getting_started/flows/) found in the given environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. ## Example - {{< tabs "approvals" "col-no-wrap" >}} {{< tab "v2" >}} @@ -48,7 +46,7 @@ See [kosli pipeline approval report](/legacy_ref/v0.1.41/kosli_pipeline_approval ## Quick note about a commit list -When reporting or requesting an approval keep in mind that `oldest-commit` has to be an ancestor of `newest-commit`. +If you are providing the oldest and newest commits yourself, keep in mind that `--oldest-commit` has to be an ancestor of `--newest-commit`. It's easy to verify locally in the repository using: ```shell {.command} diff --git a/docs.kosli.com/content/helm/_index.md b/docs.kosli.com/content/helm/_index.md index a67e3c0a6..579937da7 100644 --- a/docs.kosli.com/content/helm/_index.md +++ b/docs.kosli.com/content/helm/_index.md @@ -61,7 +61,7 @@ helm upgrade [RELEASE-NAME] kosli/k8s-reporter | kosliApiToken.secretKey | string | `""` | the name of the key in the secret data which contains the kosli API token | | kosliApiToken.secretName | string | `""` | the name of the secret containing the kosli API token | | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | -| podAnnotations | object | `{}` | | +| podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | | reporterConfig.kosliEnvironmentName | string | `""` | the name of kosli environment that the k8s cluster/namespace correlates to | | reporterConfig.kosliOrg | string | `""` | the name of the kosli org | @@ -74,5 +74,5 @@ helm upgrade [RELEASE-NAME] kosli/k8s-reporter | serviceAccount.name | string | `""` | the name of the service account to use. If not set and create is true, a name is generated using the fullname template | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) From c7055efcbf6bbe2a076a88b12c05a8118806e657 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Tue, 28 Nov 2023 11:44:53 +0100 Subject: [PATCH 15/16] Docs: fix typos --- docs.kosli.com/content/getting_started/approvals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs.kosli.com/content/getting_started/approvals.md b/docs.kosli.com/content/getting_started/approvals.md index 78d3370c7..68c34cddd 100644 --- a/docs.kosli.com/content/getting_started/approvals.md +++ b/docs.kosli.com/content/getting_started/approvals.md @@ -5,9 +5,9 @@ weight: 260 --- # Part 7: Approvals -Whenever a given artifact is ready to be deployed you may need an additional approval. In Kosli, an Approval means an artifact is ready to be deployed to a given [environment](/getting_started/environments/). An approval can be manually or automatically, and recorded into Kosli so the decision made outside of your CI system won't be lost. +Whenever a given artifact is ready to be deployed you may need an additional approval. In Kosli, an Approval means an artifact is ready to be deployed to a given [environment](/getting_started/environments/). An approval can be created manually or automatically, and recorded into Kosli so the decision made outside of your CI system won't be lost. -When an Approval is created for an artifact to a specific environment with the `--environment` flag, Kosli will generate a list of commits to be approved. By default, this list will contain all commits between `HEAD` and the commit of the latest artifact coming from the same [flow](/getting_started/flows/) found in the given environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. +When an Approval is created for an artifact to a specific environment with the `--environment` flag, Kosli will generate a list of commits to be approved. By default, this list will contain all commits between `HEAD` and the commit of the most recent artifact coming from the same [flow](/getting_started/flows/) found in the given environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. ## Example From 8272d2c6ae8a27f959cb9d5837b3b67b3fb6ca5b Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen Date: Wed, 29 Nov 2023 11:19:47 +0100 Subject: [PATCH 16/16] Approval: Small update docs --- cmd/kosli/reportApproval.go | 2 +- cmd/kosli/requestApproval.go | 2 +- .../content/getting_started/approvals.md | 34 ++----------------- 3 files changed, 5 insertions(+), 33 deletions(-) diff --git a/cmd/kosli/reportApproval.go b/cmd/kosli/reportApproval.go index 57bc6522f..4eaf49078 100644 --- a/cmd/kosli/reportApproval.go +++ b/cmd/kosli/reportApproval.go @@ -13,7 +13,7 @@ import ( ) const ( - reportApprovalShortDesc = `Report an approval of deploying an artifact to Kosli. ` + reportApprovalShortDesc = `Report an approval of deploying an artifact to an environment to Kosli. ` reportApprovalLongDesc = reportApprovalShortDesc + ` ` + fingerprintDesc ) diff --git a/cmd/kosli/requestApproval.go b/cmd/kosli/requestApproval.go index a71c7f44a..b7e44ac88 100644 --- a/cmd/kosli/requestApproval.go +++ b/cmd/kosli/requestApproval.go @@ -7,7 +7,7 @@ import ( ) const ( - requestApprovalShortDesc = `Request an approval of a deployment of an artifact in Kosli. ` + requestApprovalShortDesc = `Request an approval of a deployment of an artifact to an environment in Kosli. ` requestApprovalLongDesc = requestApprovalShortDesc + ` The request should be reviewed in the Kosli UI. ` + fingerprintDesc diff --git a/docs.kosli.com/content/getting_started/approvals.md b/docs.kosli.com/content/getting_started/approvals.md index 68c34cddd..6bdd39911 100644 --- a/docs.kosli.com/content/getting_started/approvals.md +++ b/docs.kosli.com/content/getting_started/approvals.md @@ -5,9 +5,10 @@ weight: 260 --- # Part 7: Approvals -Whenever a given artifact is ready to be deployed you may need an additional approval. In Kosli, an Approval means an artifact is ready to be deployed to a given [environment](/getting_started/environments/). An approval can be created manually or automatically, and recorded into Kosli so the decision made outside of your CI system won't be lost. +Whenever an artifact is ready to be deployed to a given [environment](/getting_started/environments/), an additional approval may be created in Kosli. An approval can be requested which will require a manually action, or reported automatically. This will be recorded into Kosli so the decision made outside of your CI system won't be lost. + +When an approval is created for an artifact to a specific environment with the `--environment` flag, Kosli will generate a list of commits to be approved. By default, this list will contain all commits between `HEAD` and the commit of the most recent artifact coming from the same [flow](/getting_started/flows/) found in the given environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. If you are providing these commits yourself, keep in mind that `--oldest-commit` has to be an ancestor of `--newest-commit`. -When an Approval is created for an artifact to a specific environment with the `--environment` flag, Kosli will generate a list of commits to be approved. By default, this list will contain all commits between `HEAD` and the commit of the most recent artifact coming from the same [flow](/getting_started/flows/) found in the given environment. The list can also be specified by providing values for `--newest-commit` and `--oldest-commit`. ## Example @@ -41,32 +42,3 @@ See [kosli pipeline approval report](/legacy_ref/v0.1.41/kosli_pipeline_approval {{< /tab >}} {{< /tabs >}} - -{{< hint warning >}} - -## Quick note about a commit list - -If you are providing the oldest and newest commits yourself, keep in mind that `--oldest-commit` has to be an ancestor of `--newest-commit`. - -It's easy to verify locally in the repository using: -```shell {.command} -git merge-base --is-ancestor -echo $? -``` - -`echo $?` checks the exit code of previous command so it's important you run it directly after `git merge-base <...>` command. - -Exit code 0 means `oldest-commit` is an ancestor of `newest-commit` and your kosli approval command will work. If the exit code is different than 0 then we won't be able to generate a list of commits needed for an approval and the command will fail. - -To be able to trace back the history of your commits we need a complete repository history to be available - in your CI pipelines it'll likely mean you have to explicitly check out the whole history (many CI tools checkout just a latest version by default). - -In GitHub Actions you'd need to modify the checkout step by adding fetch-depth option (zero means full depth): - -``` -steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 -``` - -{{< /hint >}}