Skip to content

Commit

Permalink
OSD-24269:Enable login to cluster via OHSS card
Browse files Browse the repository at this point in the history
  • Loading branch information
samanthajayasinghe committed Jul 11, 2024
1 parent 46901bc commit 75dc45f
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 44 deletions.
46 changes: 42 additions & 4 deletions cmd/ocm-backplane/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/openshift/backplane-cli/pkg/backplaneapi"
"github.com/openshift/backplane-cli/pkg/cli/config"
"github.com/openshift/backplane-cli/pkg/cli/globalflags"
"github.com/openshift/backplane-cli/pkg/jira"
"github.com/openshift/backplane-cli/pkg/login"
"github.com/openshift/backplane-cli/pkg/ocm"
"github.com/openshift/backplane-cli/pkg/pagerduty"
Expand All @@ -36,6 +37,7 @@ const (
LoginTypeClusterID = "cluster-id"
LoginTypeExistingKubeConfig = "kube-config"
LoginTypePagerduty = "pagerduty"
LoginTypeJira = "jira"
)

var (
Expand All @@ -44,6 +46,7 @@ var (
kubeConfigPath string
pd string
defaultNamespace string
ohss string
}

// loginType derive the login type based on flags and args
Expand All @@ -62,7 +65,7 @@ var (
run oc command later to operate the target cluster.`,
Example: " backplane login <id>\n backplane login %test%\n backplane login <external_id>\n backplane login --pd <incident-id>",
Args: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Lookup("pd").Changed {
if cmd.Flags().Lookup("pd").Changed || cmd.Flags().Lookup("ohss").Changed {
if err := cobra.ExactArgs(0)(cmd, args); err != nil {
return err
}
Expand Down Expand Up @@ -112,6 +115,12 @@ func init() {
"default",
"The default namespace for a user to execute commands in",
)
flags.StringVar(
&args.ohss,
"ohss",
"",
"Login using JIRA Id",
)

}

Expand Down Expand Up @@ -140,7 +149,16 @@ func runLogin(cmd *cobra.Command, argv []string) (err error) {
}
clusterKey = info.ClusterID
elevateReason = info.WebURL

case LoginTypeJira:
ohssIssue, err := getClusterInfoFromJira(bpConfig)
if err != nil {
return err
}
if ohssIssue.ClusterID == "" {
return fmt.Errorf("clusterID cannot be detected for JIRA issue:%s", args.ohss)
}
clusterKey = ohssIssue.ClusterID
elevateReason = ohssIssue.WebURL
case LoginTypeClusterID:
logger.Debugf("Cluster Key is given in argument")
clusterKey = argv[0]
Expand Down Expand Up @@ -571,9 +589,11 @@ func preLogin(cmd *cobra.Command, argv []string) (err error) {
loginType = LoginTypeClusterID

case 0:
if args.pd == "" {
if args.pd == "" && args.ohss == "" {
loginType = LoginTypeExistingKubeConfig
} else {
} else if args.ohss != "" {
loginType = LoginTypeJira
} else if args.pd != "" {
loginType = LoginTypePagerduty
}
}
Expand Down Expand Up @@ -606,6 +626,24 @@ func getClusterInfoFromPagerduty(bpConfig config.BackplaneConfiguration) (alert
return alert, nil
}

// getClusterInfoFromJira returns a cluster info OHSS card
func getClusterInfoFromJira(bpConfig config.BackplaneConfiguration) (ohss jira.OHSSIssue, err error) {
if bpConfig.JiraAPIToken == "" || bpConfig.JiraBaseURL == "" {
return ohss, fmt.Errorf("please make sure the JIRA url and token are configured correctly in the config file")
}
jiraClient, err := jira.NewJiraFromConfig(bpConfig)
if err != nil {
return ohss, fmt.Errorf("could not initialize the client: %w", err)
}

ohss, err = jiraClient.GetIssue(args.ohss)
if err != nil {
return ohss, err
}

return ohss, nil
}

// getClusterIDFromExistingKubeConfig returns clusterId from kubeconfig
func getClusterIDFromExistingKubeConfig() (string, error) {
var clusterKey string
Expand Down
13 changes: 11 additions & 2 deletions pkg/jira/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import (
// JiraClient is an interface to handle jira functions
type JiraClient interface {
Connect(baseURL string, jiraToken string) error
SearchIssue(jql string, options *jira.SearchOptions) (issues []jira.Issue, err error)
SearchIssues(jql string, options *jira.SearchOptions) (issues []jira.Issue, err error)
GetIssue(issueID string, options *jira.GetQueryOptions) (*jira.Issue, error)
CreateIssue(issue *jira.Issue) (*jira.Issue, error)
}

Expand Down Expand Up @@ -46,9 +47,17 @@ func (c *DefaultJiraClientImpl) Connect(baseURL string, jiraToken string) error
}

// SearchIssue returns the issues based on JQL
func (c *DefaultJiraClientImpl) SearchIssue(jql string, options *jira.SearchOptions) (issues []jira.Issue, err error) {
func (c *DefaultJiraClientImpl) SearchIssues(jql string, options *jira.SearchOptions) (issues []jira.Issue, err error) {
issues, _, err = c.client.Issue.Search(jql, options)
return issues, err

}

// SearchIssue returns the issues based on JQL
func (c *DefaultJiraClientImpl) GetIssue(issueID string, options *jira.GetQueryOptions) (*jira.Issue, error) {
issue, _, err := c.client.Issue.Get(issueID, options)
return issue, err

}

// Create Jira Issue
Expand Down
61 changes: 44 additions & 17 deletions pkg/jira/jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ import (
)

const (
JiraOHSSProjectName = "OpenShift Hosted SRE Support"
JiraOHSSProjectID = "OHSS"
CustomFieldClusterID = "customfield_12316349"
JqlGetIssueTemplate = `project = "%s" AND issue = "%s"`
JqlGetIssuesForClusterTemplate = `(project = "%s" AND "Cluster ID" ~ "%s")
OR (project = "%s" AND "Cluster ID" ~ "%s")
ORDER BY created DESC`
)

type OHSSIssue struct {
ID string
Title string
ProjectID string
WebURL string
ClusterID string
}

type Jira struct {
client JiraClient
}
Expand All @@ -27,7 +36,7 @@ func NewJira(client JiraClient) *Jira {

func NewJiraFromConfig(bpConfig config.BackplaneConfiguration) (*Jira, error) {
jiraClient := NewClient()
err := jiraClient.Connect(bpConfig.JiraBaseUrl, bpConfig.JiraAPIToken)
err := jiraClient.Connect(bpConfig.JiraBaseURL, bpConfig.JiraAPIToken)

if err != nil {
return nil, err
Expand All @@ -40,24 +49,26 @@ func NewJiraFromConfig(bpConfig config.BackplaneConfiguration) (*Jira, error) {
}

// GetIssue returns matching issue from OHSS project
func (j *Jira) GetIssue(issueID string) (issue jira.Issue, err error) {
func (j *Jira) GetIssue(issueID string) (ohssIssue OHSSIssue, err error) {

if issueID == "" {
return issue, fmt.Errorf("empty issue Id")
return ohssIssue, fmt.Errorf("empty issue Id")
}
jql := fmt.Sprintf(JqlGetIssueTemplate, JiraOHSSProjectName, issueID)
issues, err := j.client.SearchIssue(jql, nil)
issue, err := j.client.GetIssue(issueID, nil)
if err != nil {
return issue, err
return ohssIssue, err
}
switch len(issues) {
case 0:
return issue, fmt.Errorf("no matching issue for issueID:%s", issueID)
case 1:
return issues[0], nil
default:
return issue, fmt.Errorf("more than one matching issues for issueID:%s", issueID)
if issue != nil {
formatIssue, err := j.formatIssue(*issue)
if err != nil {
return ohssIssue, err
}
return formatIssue, nil
} else {
return ohssIssue, fmt.Errorf("no matching issue for issueID:%s", issueID)

}

}

// GetJiraIssuesForCluster returns all matching issues for cluster ID
Expand All @@ -69,14 +80,30 @@ func (j *Jira) GetJiraIssuesForCluster(clusterID string, externalClusterID strin

jql := fmt.Sprintf(
JqlGetIssuesForClusterTemplate,
JiraOHSSProjectName,
JiraOHSSProjectID,
clusterID,
JiraOHSSProjectName,
JiraOHSSProjectID,
externalClusterID,
)
issues, err := j.client.SearchIssue(jql, nil)
issues, err := j.client.SearchIssues(jql, nil)
if err != nil {
return nil, err
}
return issues, nil
}

// formatIssue for
func (j *Jira) formatIssue(issue jira.Issue) (formatIssue OHSSIssue, err error) {

formatIssue.ID = issue.ID
if issue.Fields != nil {
clusterID, clusterIDExist := issue.Fields.Unknowns[CustomFieldClusterID]
if clusterIDExist {
formatIssue.ClusterID = fmt.Sprintf("%s", clusterID)
}
}
formatIssue.WebURL = issue.Self
formatIssue.Title = issue.Fields.Summary
formatIssue.ProjectID = issue.Fields.Project.ID
return formatIssue, nil
}
19 changes: 4 additions & 15 deletions pkg/jira/jira_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package jira

import (
"fmt"

"github.com/andygrunwald/go-jira"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
Expand All @@ -17,7 +15,7 @@ var _ = Describe("Jira", func() {
jiraClient *Jira
testOHSSID string
testIssue jira.Issue
testGetJql string
//testGetJql string
)

BeforeEach(func() {
Expand All @@ -26,7 +24,7 @@ var _ = Describe("Jira", func() {
jiraClient = NewJira(mockJiraClient)
testOHSSID = "OHSS-1000"
testIssue = jira.Issue{ID: testOHSSID}
testGetJql = fmt.Sprintf(JqlGetIssueTemplate, JiraOHSSProjectName, testOHSSID)
//testGetJql = fmt.Sprintf(JqlGetIssueTemplate, JiraOHSSProjectName, testOHSSID)

})

Expand All @@ -37,24 +35,15 @@ var _ = Describe("Jira", func() {
Context("When Jira client executes", func() {
It("Should return one issue", func() {

mockJiraClient.EXPECT().SearchIssue(testGetJql, nil).Return([]jira.Issue{testIssue}, nil).Times(1)
mockJiraClient.EXPECT().GetIssue(testOHSSID, nil).Return(&testIssue, nil).Times(1)

issue, err := jiraClient.GetIssue(testOHSSID)
Expect(err).To(BeNil())
Expect(issue.ID).To(Equal(testOHSSID))
})
It("Should return error for multiple matching issues", func() {

returnIssues := []jira.Issue{testIssue, {ID: testOHSSID, Key: "Test Name 2"}}
mockJiraClient.EXPECT().SearchIssue(testGetJql, nil).Return(returnIssues, nil).Times(1)

_, err := jiraClient.GetIssue(testOHSSID)
Expect(err).NotTo(BeNil())
Expect(err.Error()).To(Equal("more than one matching issues for issueID:OHSS-1000"))
})
It("Should return error for empty issue", func() {

mockJiraClient.EXPECT().SearchIssue(testGetJql, nil).Return([]jira.Issue{}, nil).Times(1)
mockJiraClient.EXPECT().GetIssue(testOHSSID, nil).Return(nil, nil).Times(1)

_, err := jiraClient.GetIssue(testOHSSID)
Expect(err).NotTo(BeNil())
Expand Down
27 changes: 21 additions & 6 deletions pkg/jira/mocks/clientMock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 75dc45f

Please sign in to comment.