Skip to content

Commit

Permalink
OSD-24127: New accessrequest subcommand used to manage access request…
Browse files Browse the repository at this point in the history
…s used by the lockbox feature
  • Loading branch information
Nikokolas3270 committed Jul 10, 2024
1 parent d7482fe commit 8f700a7
Show file tree
Hide file tree
Showing 17 changed files with 1,395 additions and 14 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ mock-gen:
mockgen -destination=./pkg/cli/session/mocks/sessionMock.go -package=mocks github.com/openshift/backplane-cli/pkg/cli/session BackplaneSessionInterface
mockgen -destination=./pkg/utils/mocks/shellCheckerMock.go -package=mocks github.com/openshift/backplane-cli/pkg/utils ShellCheckerInterface
mockgen -destination=./pkg/pagerduty/mocks/clientMock.go -package=mocks github.com/openshift/backplane-cli/pkg/pagerduty PagerDutyClient
mockgen -destination=./pkg/utils/mocks/jiraMock.go -package=mocks github.com/openshift/backplane-cli/pkg/utils IssueServiceInterface

.PHONY: build-image
build-image:
Expand Down
26 changes: 26 additions & 0 deletions cmd/ocm-backplane/accessrequest/accessRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package accessrequest

import (
"github.com/spf13/cobra"
)

func NewAccessRequestCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "accessrequest",
Aliases: []string{"accessRequest", "accessrequest", "accessrequests"},
Short: "Manages access requests for clusters on which access protection is enabled",
SilenceUsage: true,
}

// cluster-id Flag
cmd.PersistentFlags().StringP("cluster-id", "c", "", "Cluster ID could be cluster name, id or external-id")

cmd.AddCommand(newCreateAccessRequestCmd())
cmd.AddCommand(newGetAccessRequestCmd())
cmd.AddCommand(newExpireAccessRequestCmd())

return cmd
}

func init() {
}
134 changes: 134 additions & 0 deletions cmd/ocm-backplane/accessrequest/createAccessRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package accessrequest

import (
"errors"
"fmt"
"strings"
"time"

"github.com/openshift/backplane-cli/pkg/accessrequest"

ocmcli "github.com/openshift-online/ocm-cli/pkg/ocm"
"github.com/openshift/backplane-cli/pkg/login"
"github.com/openshift/backplane-cli/pkg/utils"
logger "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var (
options struct {
reason string
notificationIssueID string
pendingDuration time.Duration
approvalDuration time.Duration
}
)

// newCreateAccessRequestCmd returns cobra command
func newCreateAccessRequestCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Creates a new pending access request",
Args: cobra.ExactArgs(0),
SilenceUsage: true,
SilenceErrors: true,
RunE: runCreateAccessRequest,
}

cmd.Flags().StringVarP(
&options.reason,
"reason",
"r",
"",
"Reason/justification passed through the access request to the customer. "+
"Reason will be read from the kube context (unless --cluster-id is set) or prompted if the option is not set.")

cmd.Flags().StringVarP(
&options.notificationIssueID,
"notification-issue",
"n",
"",
"JIRA issue used for notifications when the access request is approved or denied. "+
"Issue needs to belong to the OHSS project on production and to the SDAINT project for staging & integration. "+
"Issue will automatically be created in the proper project if the option is not set.")

cmd.Flags().DurationVarP(
&options.approvalDuration,
"approval-duration",
"d",
8*time.Hour,
"The maximal period of time during which the access request can stay approved")

return cmd
}

func retrieveOrPromptReason(cmd *cobra.Command) string {
if utils.CheckValidPrompt() {
clusterKey, err := cmd.Flags().GetString("cluster-id")

if err == nil && clusterKey == "" {
config, err := utils.ReadKubeconfigRaw()

if err == nil {
reasons := login.GetElevateContextReasons(config)
for _, reason := range reasons {
if reason != "" {
fmt.Printf("Reason for elevations read from the kube config: %s\n", reason)
if strings.ToLower(utils.AskQuestionFromPrompt("Do you want to use this as the reason/justification for the access request to create (Y/n)? ")) != "n" {
return reason
}
break
}
}
} else {
logger.Warnf("won't extract the elevation reason from the kube context which failed to be read: %v", err)
}
}
}

return utils.AskQuestionFromPrompt("Please enter a reason/justification for the access request to create: ")
}

// runCreateAccessRequest creates access request for the given cluster
func runCreateAccessRequest(cmd *cobra.Command, args []string) error {
clusterID, err := accessrequest.GetClusterID(cmd)
if err != nil {
return fmt.Errorf("failed to compute cluster ID: %v", err)
}

ocmConnection, err := ocmcli.NewConnection().Build()
if err != nil {
return fmt.Errorf("failed to create OCM connection: %v", err)
}
defer ocmConnection.Close()

accessRequest, err := accessrequest.GetAccessRequest(ocmConnection, clusterID)

if err != nil {
return err
}

if accessRequest != nil {
accessrequest.PrintAccessRequest(clusterID, accessRequest)

return fmt.Errorf("there is already an active access request for cluster '%s', eventually consider expiring it running 'ocm-backplane accessrequest expire'", clusterID)
}

reason := options.reason
if reason == "" {
reason = retrieveOrPromptReason(cmd)
if reason == "" {
return errors.New("no reason/justification, consider using the --reason option with a non empty string")
}
}

accessRequest, err = accessrequest.CreateAccessRequest(ocmConnection, clusterID, reason, options.notificationIssueID, options.approvalDuration)

if err != nil {
return err
}

accessrequest.PrintAccessRequest(clusterID, accessRequest)

return nil
}
57 changes: 57 additions & 0 deletions cmd/ocm-backplane/accessrequest/expireAccessRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package accessrequest

import (
"fmt"

"github.com/openshift/backplane-cli/pkg/accessrequest"

ocmcli "github.com/openshift-online/ocm-cli/pkg/ocm"
"github.com/spf13/cobra"
)

// newExpireAccessRequestCmd returns cobra command
func newExpireAccessRequestCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "expire",
Short: "Expire the active (pending or approved) access request",
Args: cobra.ExactArgs(0),
SilenceUsage: true,
SilenceErrors: true,
RunE: runExpireAccessRequest,
}

return cmd
}

// runExpireAccessRequest retrieves the active access request and expire it
func runExpireAccessRequest(cmd *cobra.Command, args []string) error {
clusterID, err := accessrequest.GetClusterID(cmd)
if err != nil {
return fmt.Errorf("failed to compute cluster ID: %v", err)
}

ocmConnection, err := ocmcli.NewConnection().Build()
if err != nil {
return fmt.Errorf("failed to create OCM connection: %v", err)
}
defer ocmConnection.Close()

accessRequest, err := accessrequest.GetAccessRequest(ocmConnection, clusterID)

if err != nil {
return fmt.Errorf("failed to retrieve access request: %v", err)
}

if accessRequest == nil {
return fmt.Errorf("no pending or approved access request for cluster '%s'", clusterID)
}

err = accessrequest.ExpireAccessRequest(ocmConnection, accessRequest)
if err != nil {
return err
}

fmt.Printf("Access request '%s' has been expired\n", accessRequest.HREF())

return nil
}
54 changes: 54 additions & 0 deletions cmd/ocm-backplane/accessrequest/getAccessRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package accessrequest

import (
"fmt"

"github.com/openshift/backplane-cli/pkg/accessrequest"

ocmcli "github.com/openshift-online/ocm-cli/pkg/ocm"
logger "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

// newGetAccessRequestCmd returns cobra command
func newGetAccessRequestCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: "Get the active (pending or approved) access request",
Args: cobra.ExactArgs(0),
SilenceUsage: true,
SilenceErrors: true,
RunE: runGetAccessRequest,
}

return cmd
}

// runGetAccessRequest retrieves the active access request and print it
func runGetAccessRequest(cmd *cobra.Command, args []string) error {
clusterID, err := accessrequest.GetClusterID(cmd)
if err != nil {
return fmt.Errorf("failed to compute cluster ID: %v", err)
}

ocmConnection, err := ocmcli.NewConnection().Build()
if err != nil {
return fmt.Errorf("failed to create OCM connection: %v", err)
}
defer ocmConnection.Close()

accessRequest, err := accessrequest.GetAccessRequest(ocmConnection, clusterID)

if err != nil {
return err
}

if accessRequest == nil {
logger.Warnf("no pending or approved access request for cluster '%s'", clusterID)
fmt.Printf("To get denied or expired access requests, run: ocm get /api/access_transparency/v1/access_requests -p search=\"cluster_id='%s'\"\n", clusterID)
} else {
accessrequest.PrintAccessRequest(clusterID, accessRequest)
}

return nil
}
6 changes: 5 additions & 1 deletion cmd/ocm-backplane/config/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func setConfig(cmd *cobra.Command, args []string) error {
}

bpConfig.SessionDirectory = viper.GetString("session-dir")
bpConfig.JiraToken = viper.GetString(config.JiraTokenViperKey)
}

// create config directory if it doesn't exist
Expand Down Expand Up @@ -90,15 +91,18 @@ func setConfig(cmd *cobra.Command, args []string) error {
bpConfig.SessionDirectory = args[1]
case PagerDutyAPIConfigVar:
bpConfig.PagerDutyAPIKey = args[1]
case config.JiraTokenViperKey:
bpConfig.JiraToken = args[1]
default:
return fmt.Errorf("supported config variables are %s, %s, %s & %s", URLConfigVar, ProxyURLConfigVar, SessionConfigVar, PagerDutyAPIConfigVar)
return fmt.Errorf("supported config variables are %s, %s, %s, %s & %s", URLConfigVar, ProxyURLConfigVar, SessionConfigVar, PagerDutyAPIConfigVar, config.JiraTokenViperKey)
}

viper.SetConfigType("json")
viper.Set(URLConfigVar, bpConfig.URL)
viper.Set(ProxyURLConfigVar, bpConfig.ProxyURL)
viper.Set(SessionConfigVar, bpConfig.SessionDirectory)
viper.Set(PagerDutyAPIConfigVar, bpConfig.PagerDutyAPIKey)
viper.Set(config.JiraTokenViperKey, bpConfig.JiraToken)

err = viper.WriteConfigAs(configPath)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/ocm-backplane/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/openshift/backplane-cli/cmd/ocm-backplane/accessrequest"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/cloud"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/config"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/console"
Expand Down Expand Up @@ -64,6 +65,7 @@ func init() {
globalflags.AddVerbosityFlag(rootCmd)

// Register sub-commands
rootCmd.AddCommand(accessrequest.NewAccessRequestCmd())
rootCmd.AddCommand(console.NewConsoleCmd())
rootCmd.AddCommand(config.NewConfigCmd())
rootCmd.AddCommand(cloud.CloudCmd)
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
github.com/Masterminds/semver v1.5.0
github.com/PagerDuty/go-pagerduty v1.8.0
github.com/andygrunwald/go-jira v1.16.0
github.com/aws/aws-sdk-go-v2 v1.30.1
github.com/aws/aws-sdk-go-v2/config v1.27.24
github.com/aws/aws-sdk-go-v2/credentials v1.17.24
Expand Down Expand Up @@ -59,6 +60,7 @@ require (
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/getkin/kin-openapi v0.113.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
Expand Down Expand Up @@ -121,6 +123,7 @@ require (
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/trivago/tgo v1.0.7 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/zalando/go-keyring v0.2.3 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
Expand Down
Loading

0 comments on commit 8f700a7

Please sign in to comment.