Skip to content

Commit

Permalink
OSD-24268: Determine backplane elevate reason automatically when logi…
Browse files Browse the repository at this point in the history
…n via pagerduty incidents (#441)

* Refactor elevate command and move kubeconfig func to kubeconfig module

* refactor login command to seperate login types

* OSD-22250: Added backplane elevate reason when login via pagerduty

* remove unwanted config params

* fixed lint

* add elevate reason to config file
  • Loading branch information
samanthajayasinghe committed Jul 10, 2024
1 parent d82f134 commit d7482fe
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 250 deletions.
130 changes: 95 additions & 35 deletions cmd/ocm-backplane/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import (
)

// Environment variable that for setting PS1
const EnvPs1 = "KUBE_PS1_CLUSTER_FUNCTION"
const (
EnvPs1 = "KUBE_PS1_CLUSTER_FUNCTION"
LoginTypeClusterID = "cluster-id"
LoginTypeExistingKubeConfig = "kube-config"
LoginTypePagerduty = "pagerduty"
)

var (
args struct {
Expand All @@ -41,6 +46,10 @@ var (
defaultNamespace string
}

// loginType derive the login type based on flags and args
// set default login type as cluster-id
loginType = LoginTypeClusterID

globalOpts = &globalflags.GlobalOptions{}

// LoginCmd represents the login command
Expand All @@ -64,6 +73,7 @@ var (
}
return nil
},
PreRunE: preLogin,
RunE: runLogin,
SilenceUsage: true,
}
Expand Down Expand Up @@ -93,7 +103,7 @@ func init() {
&args.pd,
"pd",
"",
"Login using PagerDuty incident id or html_url.",
"Login using PagerDuty incident id or pagerduty url.",
)
flags.StringVarP(
&args.defaultNamespace,
Expand All @@ -107,6 +117,7 @@ func init() {

func runLogin(cmd *cobra.Command, argv []string) (err error) {
var clusterKey string
var elevateReason string
logger.Debugf("Running Login Command ...")
logger.Debugf("Checking Backplane Version")
utils.CheckBackplaneVersion(cmd)
Expand All @@ -119,47 +130,29 @@ func runLogin(cmd *cobra.Command, argv []string) (err error) {
}
logger.Debugf("Backplane Config File Contains: %v \n", bpConfig)

// login to the cluster based on login type
logger.Debugf("Extracting Backplane Cluster ID")
// Currently go-pagerduty pkg does not include incident id validation.
if args.pd != "" {
if bpConfig.PagerDutyAPIKey == "" {
return fmt.Errorf("please make sure the PD API Key is configured correctly in the config file")
}
pdClient, err := pagerduty.NewWithToken(bpConfig.PagerDutyAPIKey)
switch loginType {
case LoginTypePagerduty:
info, err := getClusterInfoFromPagerduty(bpConfig)
if err != nil {
return fmt.Errorf("could not initialize the client: %w", err)
}
if strings.Contains(args.pd, "/incidents/") {
incidentID := args.pd[strings.LastIndex(args.pd, "/")+1:]
clusterKey, err = pdClient.GetClusterIDFromIncident(incidentID)
if err != nil {
return err
}
} else {
clusterKey, err = pdClient.GetClusterIDFromIncident(args.pd)
if err != nil {
return err
}
return err
}
}
clusterKey = info.ClusterID
elevateReason = info.WebURL

// Get the cluster ID only if it hasn't been populated by PagerDuty.
if len(argv) == 1 {
// if explicitly one cluster key given, use it to log in.
case LoginTypeClusterID:
logger.Debugf("Cluster Key is given in argument")
clusterKey = argv[0]
logger.WithField("Search Key", clusterKey).Debugln("Finding target cluster")

} else if len(argv) == 0 && args.pd == "" {
// if no args given, try to log into the cluster that the user is logged into
logger.Debugf("Finding Clustrer Key from current cluster")
clusterInfo, err := utils.DefaultClusterUtils.GetBackplaneClusterFromConfig()
case LoginTypeExistingKubeConfig:
clusterKey, err = getClusterIDFromExistingKubeConfig()
if err != nil {
return err
}
clusterKey = clusterInfo.ClusterID
logger.Debugf("Backplane Cluster Infromation data extracted: %+v\n", clusterInfo)
default:
return fmt.Errorf("login type cannot be detected")
}

logger.Debugf("Backplane Cluster Key is: %v \n", clusterKey)

logger.Debugln("Setting Proxy URL from global options")
Expand Down Expand Up @@ -353,11 +346,22 @@ func runLogin(cmd *cobra.Command, argv []string) (err error) {
rc.Contexts[targetContextNickName] = targetContext
rc.CurrentContext = targetContextNickName

// Add elevate reason to kubeconfig context
if elevateReason != "" {
elevationReasons, err := login.SaveElevateContextReasons(rc, elevateReason)
if err != nil {
return err
}
logger.Infof("save elevate reason: %s\n", elevationReasons)
}

logger.Debugln("Saving new API config")
// Save the config
err = login.SaveKubeConfig(clusterID, rc, args.multiCluster, args.kubeConfigPath)
if err = login.SaveKubeConfig(clusterID, rc, args.multiCluster, args.kubeConfigPath); err != nil {
return err
}

return err
return nil
}

// GetRestConfig returns a client-go *rest.Config which can be used to programmatically interact with the
Expand Down Expand Up @@ -558,3 +562,59 @@ func isValidKubernetesNamespace(namespace string) bool {
pattern := `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
return regexp.MustCompile(pattern).MatchString(namespace)
}

// preLogin will execute before the command
func preLogin(cmd *cobra.Command, argv []string) (err error) {

switch len(argv) {
case 1:
loginType = LoginTypeClusterID

case 0:
if args.pd == "" {
loginType = LoginTypeExistingKubeConfig
} else {
loginType = LoginTypePagerduty
}
}

return nil
}

// getClusterInfoFromPagerduty returns a pagerduty.Alert from Pagerduty incident,
// which contains alert info including the cluster id.
func getClusterInfoFromPagerduty(bpConfig config.BackplaneConfiguration) (alert pagerduty.Alert, err error) {
if bpConfig.PagerDutyAPIKey == "" {
return alert, fmt.Errorf("please make sure the PD API Key is configured correctly in the config file")
}
pdClient, err := pagerduty.NewWithToken(bpConfig.PagerDutyAPIKey)
if err != nil {
return alert, fmt.Errorf("could not initialize the client: %w", err)
}
if strings.Contains(args.pd, "/incidents/") {
incidentID := args.pd[strings.LastIndex(args.pd, "/")+1:]
alert, err = pdClient.GetClusterInfoFromIncident(incidentID)
if err != nil {
return alert, err
}
} else {
alert, err = pdClient.GetClusterInfoFromIncident(args.pd)
if err != nil {
return alert, err
}
}
return alert, nil
}

// getClusterIDFromExistingKubeConfig returns clusterId from kubeconfig
func getClusterIDFromExistingKubeConfig() (string, error) {
var clusterKey string
logger.Debugf("Finding Clustrer Key from current cluster")
clusterInfo, err := utils.DefaultClusterUtils.GetBackplaneClusterFromConfig()
if err != nil {
return "", err
}
clusterKey = clusterInfo.ClusterID
logger.Debugf("Backplane Cluster Infromation data extracted: %+v\n", clusterInfo)
return clusterKey, nil
}
23 changes: 13 additions & 10 deletions cmd/ocm-backplane/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ var _ = Describe("Login command", func() {
mockCluster = &cmv1.Cluster{}

backplaneConfiguration = config.BackplaneConfiguration{URL: backplaneAPIURI}

loginType = LoginTypeClusterID
})

AfterEach(func() {
Expand Down Expand Up @@ -184,6 +186,7 @@ var _ = Describe("Login command", func() {
err := utils.CreateTempKubeConfig(nil)
Expect(err).To(BeNil())
globalOpts.ProxyURL = "https://squid.myproxy.com"
os.Setenv("HTTPS_PROXY", "https://squid.myproxy.com")
mockOcmInterface.EXPECT().GetOCMEnvironment().Return(ocmEnv, nil).AnyTimes()
mockClientUtil.EXPECT().SetClientProxyURL(globalOpts.ProxyURL).Return(nil)
mockOcmInterface.EXPECT().GetTargetCluster(testClusterID).Return(trueClusterID, testClusterID, nil)
Expand Down Expand Up @@ -345,6 +348,7 @@ var _ = Describe("Login command", func() {
})

It("should login to current cluster if cluster id not provided", func() {
loginType = LoginTypeExistingKubeConfig
err := utils.CreateTempKubeConfig(nil)
Expect(err).To(BeNil())
globalOpts.ProxyURL = "https://squid.myproxy.com"
Expand Down Expand Up @@ -431,6 +435,7 @@ var _ = Describe("Login command", func() {
})

It("should fail to create PD API client and return HTTP status code 401 when unauthorized", func() {
loginType = LoginTypePagerduty
args.pd = truePagerDutyIncidentID

err := utils.CreateTempKubeConfig(nil)
Expand Down Expand Up @@ -466,6 +471,7 @@ var _ = Describe("Login command", func() {
})

It("should return error when trying to login via PD but the PD API Key is not configured", func() {
loginType = LoginTypePagerduty
args.pd = truePagerDutyIncidentID

err := utils.CreateTempKubeConfig(nil)
Expand All @@ -480,11 +486,9 @@ var _ = Describe("Login command", func() {
Expect(err).To(BeNil())

testData := config.BackplaneConfiguration{
URL: backplaneAPIURI,
ProxyURL: new(string),
SessionDirectory: "",
AssumeInitialArn: "",
PagerDutyAPIKey: falsePagerDutyAPITkn,
URL: backplaneAPIURI,
ProxyURL: new(string),
PagerDutyAPIKey: falsePagerDutyAPITkn,
}

// Marshal the testData into JSON format and write it to tempFile.
Expand All @@ -503,6 +507,7 @@ var _ = Describe("Login command", func() {
})

It("should fail to find a non existent PD Incident and return HTTP status code 404 when the requested resource is not found", func() {
loginType = LoginTypePagerduty
args.pd = falsePagerDutyIncidentID

err := utils.CreateTempKubeConfig(nil)
Expand All @@ -517,11 +522,9 @@ var _ = Describe("Login command", func() {
Expect(err).To(BeNil())

testData := config.BackplaneConfiguration{
URL: backplaneAPIURI,
ProxyURL: new(string),
SessionDirectory: "",
AssumeInitialArn: "",
PagerDutyAPIKey: truePagerDutyAPITkn,
URL: backplaneAPIURI,
ProxyURL: new(string),
PagerDutyAPIKey: truePagerDutyAPITkn,
}

// Marshal the testData into JSON format and write it to tempFile.
Expand Down
33 changes: 3 additions & 30 deletions pkg/elevate/elevate.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package elevate

import (
"errors"
"fmt"
"os"
"os/exec"

logger "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd/api"

"github.com/openshift/backplane-cli/pkg/login"
"github.com/openshift/backplane-cli/pkg/utils"
)

Expand All @@ -19,32 +18,6 @@ var (
WriteKubeconfigToFile = utils.CreateTempKubeConfig
)

func AddElevationReasonToRawKubeconfig(config api.Config, elevationReason string) error {
return AddElevationReasonsToRawKubeconfig(config, []string{elevationReason})
}

func AddElevationReasonsToRawKubeconfig(config api.Config, elevationReasons []string) error {
logger.Debugln("Adding reason for backplane-cluster-admin elevation")
if config.Contexts[config.CurrentContext] == nil {
return errors.New("no current kubeconfig context")
}

currentCtxUsername := config.Contexts[config.CurrentContext].AuthInfo

if config.AuthInfos[currentCtxUsername] == nil {
return errors.New("no current user information")
}

if config.AuthInfos[currentCtxUsername].ImpersonateUserExtra == nil {
config.AuthInfos[currentCtxUsername].ImpersonateUserExtra = make(map[string][]string)
}

config.AuthInfos[currentCtxUsername].ImpersonateUserExtra["reason"] = elevationReasons
config.AuthInfos[currentCtxUsername].Impersonate = "backplane-cluster-admin"

return nil
}

func RunElevate(argv []string) error {
logger.Debugln("Finding target cluster from kubeconfig")
config, err := ReadKubeConfigRaw()
Expand All @@ -59,7 +32,7 @@ func RunElevate(argv []string) error {
} else {
elevateReason = argv[0]
}
elevationReasons, err := ComputeElevateContextAndStoreToKubeConfigFileAndGetReasons(config, elevateReason)
elevationReasons, err := login.SaveElevateContextReasons(config, elevateReason)
if err != nil {
return err
}
Expand All @@ -70,7 +43,7 @@ func RunElevate(argv []string) error {
}

logger.Debug("Adding impersonation RBAC allow permissions to kubeconfig")
err = AddElevationReasonsToRawKubeconfig(config, elevationReasons)
err = login.AddElevationReasonsToRawKubeconfig(config, elevationReasons)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit d7482fe

Please sign in to comment.