Skip to content

Commit

Permalink
Merge pull request #581 from itsmitul9/osd-23280
Browse files Browse the repository at this point in the history
OSD-23280 Osdctl configuration setup
  • Loading branch information
openshift-merge-bot[bot] authored Jun 28, 2024
2 parents b8a4413 + 234005b commit 8350504
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 2 deletions.
5 changes: 3 additions & 2 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import (
"github.com/openshift/osdctl/cmd/cost"
"github.com/openshift/osdctl/cmd/env"
"github.com/openshift/osdctl/cmd/hive"
"github.com/openshift/osdctl/cmd/iampermissions"
"github.com/openshift/osdctl/cmd/jira"
"github.com/openshift/osdctl/cmd/jumphost"
"github.com/openshift/osdctl/cmd/mc"
"github.com/openshift/osdctl/cmd/network"
"github.com/openshift/osdctl/cmd/org"
"github.com/openshift/osdctl/cmd/promote"
"github.com/openshift/osdctl/cmd/servicelog"
"github.com/openshift/osdctl/cmd/setup"
"github.com/openshift/osdctl/internal/utils/globalflags"
"github.com/openshift/osdctl/pkg/k8s"
"github.com/openshift/osdctl/pkg/provider/aws"
Expand Down Expand Up @@ -98,7 +98,8 @@ func NewCmdRoot(streams genericclioptions.IOStreams) *cobra.Command {
rootCmd.AddCommand(promote.NewCmdPromote())
rootCmd.AddCommand(jira.Cmd)
rootCmd.AddCommand(cloudtrail.NewCloudtrailCmd())
rootCmd.AddCommand(iampermissions.NewCmdIamPermissions())
rootCmd.AddCommand(setup.NewCmdSetup())

// Add cost command to use AWS Cost Manager
rootCmd.AddCommand(cost.NewCmdCost(streams, globalOpts))

Expand Down
230 changes: 230 additions & 0 deletions cmd/setup/setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package setup

import (
"bufio"
"errors"
"fmt"
"os"
"regexp"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/viper"
)

const (
ProdJumproleConfigKey = "prod_jumprole_account_id"
AwsProxy = "aws_proxy"
StageJumproleConfigKey = "stage_jumprole_account_id"
PdUserToken = "pd_user_token"
JiraToken = "jira_token"
DtVaultPath = "dt_vault_path"
VaultAddress = "vault_address"
CloudTrailCmdLists = "cloudtrail_cmd_lists"
JiraTokenRegex = "^[A-Z0-9]{7}$"
PdTokenRegex = "^[a-zA-Z0-9+_-]{20}$"
AwsAccountRegex = "^[0-9]{12}$"
AWSProxyRegex = `^http:\/\/[a-zA-Z0-9.-]+(:\d+)?$`
VaultURLRegex = `^https:\/\/[a-zA-Z0-9.-]+\/?$`
DtVaultPathRegex = `^[a-zA-Z0-9\-/]+$`
CloudTrailCmdListsRegex = `^\s*-\s+.*$`
)

// NewCmdSetup implements the setup command
func NewCmdSetup() *cobra.Command {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Setup the configuration",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
keys := []string{
ProdJumproleConfigKey,
AwsProxy,
StageJumproleConfigKey,
}

optionalKeys := []string{
DtVaultPath,
VaultAddress,
PdUserToken,
JiraToken,
CloudTrailCmdLists,
}

values := make(map[string]string)
reader := bufio.NewReader(os.Stdin)

defaults := make(map[string]string)
for _, key := range keys {
defaultValue := viper.GetString(key)
defaults[key] = defaultValue
}

for _, key := range optionalKeys {
defaultValue := viper.GetString(key)
defaults[key] = defaultValue
}

for _, key := range keys {
defaultValue := defaults[key]
fmt.Printf("\033[91mEnter %s \033[0m [\033[94mdefault %s\033[0m]:", key, defaultValue)
value, _ := reader.ReadString('\n')
value = strings.TrimSpace(value)

if value == "" {
value = defaultValue
}

var err error
switch key {
case JiraToken:
if value != "" && value != defaultValue {
_, err = ValidateJiraToken(value)
}
case PdUserToken:
if value != "" && value != defaultValue {
_, err = ValidatePDToken(value)
}
case ProdJumproleConfigKey, StageJumproleConfigKey:
if value != "" && value != defaultValue {
_, err = ValidateAWSAccount(value)
}
case AwsProxy:
if value != "" && value != defaultValue {
_, err = ValidateAWSProxy(value)
}
case VaultAddress:
if value != "" && value != defaultValue {
_, err = ValidateVaultAddress(value)
}
case DtVaultPath:
if value != "" && value != defaultValue {
_, err = ValidateDtVaultPath(value)
}
case CloudTrailCmdLists:
if value != "" && value != defaultValue {
_, err = ValidateCloudTrailCmdLists(value)
}
}

if err != nil {
return err
}

values[key] = value
}

for _, key := range optionalKeys {
defaultValue := defaults[key]
fmt.Printf("\033[91mEnter %s (optional)\033[0m [\033[94mdefault %s\033[0m]:", key, defaultValue)
value, _ := reader.ReadString('\n')
value = strings.TrimSpace(value)

if value == "" {
value = defaultValue
}

if value != "" && value != defaultValue {
values[key] = value
}
}

// Store the value in the config file
for key, value := range values {
viper.Set(key, value)
}
err := viper.WriteConfig()
if err != nil {
return err
}

fmt.Println("Configuration saved successfully")
return nil
},
}
return setupCmd
}

func ValidateJiraToken(token string) (string, error) {
token = strings.TrimSpace(token)
match, err := regexp.MatchString(JiraTokenRegex, token)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid jira token")
}
return token, nil
}

func ValidatePDToken(token string) (string, error) {
token = strings.TrimSpace(token)
match, err := regexp.MatchString(PdTokenRegex, token)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid pd token")
}
return token, nil
}

func ValidateAWSAccount(account string) (string, error) {
account = strings.TrimSpace(account)
match, err := regexp.MatchString(AwsAccountRegex, account)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid AWS account number")
}
return account, nil
}

func ValidateAWSProxy(proxyURL string) (string, error) {
proxyURL = strings.TrimSpace(proxyURL)
match, err := regexp.MatchString(AWSProxyRegex, proxyURL)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid AWS proxy URL")
}
return proxyURL, nil
}

func ValidateVaultAddress(vaultURL string) (string, error) {
vaultURL = strings.TrimSpace(vaultURL)
match, err := regexp.MatchString(VaultURLRegex, vaultURL)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid Vault URL")
}
return vaultURL, nil
}

func ValidateDtVaultPath(dtVaultPath string) (string, error) {
dtVaultPath = strings.TrimSpace(dtVaultPath)
match, err := regexp.MatchString(DtVaultPathRegex, dtVaultPath)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid DtVault Path")
}
return dtVaultPath, nil
}

func ValidateCloudTrailCmdLists(cloudTrailCmd string) (string, error) {
cloudTrailCmd = strings.TrimSpace(cloudTrailCmd)
match, err := regexp.MatchString(CloudTrailCmdListsRegex, cloudTrailCmd)
if err != nil {
return "", err
}
if !match {
return "", errors.New("invalid CloudTrail command")
}
return cloudTrailCmd, nil
}
93 changes: 93 additions & 0 deletions cmd/setup/setup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package setup

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestSetup(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Setup Suite")
}

var _ = Describe("Validation Functions", func() {
Context("Jira Token", func() {
It("should validate correct Jira token", func() {
token, _ := ValidateJiraToken("ABC1234")
//Expect(err).To(BeNil())
Expect(token).To(Equal("ABC1234"))
})

It("should fail invalid Jira token", func() {
_, err := ValidateJiraToken("invalid") // this should fail since "INVALID" does not match ^[A-Z0-9]{7}$
Expect(err).To(HaveOccurred())
})
})

Context("PD Token", func() {
It("should validate correct PD token", func() {
token, err := ValidatePDToken("abcdEFGHijklMNOPqrst")
Expect(err).To(BeNil())
Expect(token).To(Equal("abcdEFGHijklMNOPqrst"))
})

It("should fail invalid PD token", func() {
_, err := ValidatePDToken("short") // this should fail since "short" does not match ^[a-zA-Z0-9+_-]{20}$
Expect(err).To(HaveOccurred())
})
})

Context("AWS Account", func() {
It("should validate correct AWS account", func() {
account, err := ValidateAWSAccount("123456789012")
Expect(err).To(BeNil())
Expect(account).To(Equal("123456789012"))
})

It("should fail invalid AWS account", func() {
_, err := ValidateAWSAccount("invalid123") // this should fail since "invalid123" does not match ^[0-9]{12}$
Expect(err).To(HaveOccurred())
})
})

Context("AWS Proxy", func() {
It("should validate the correct aws proxy", func() {
proxyURL, err := ValidateAWSProxy("http://www.example.com:1234")
Expect(err).To(BeNil())
Expect(proxyURL).To(Equal("http://www.example.com:1234"))
})

It("should fail invalid proxy url", func() {
_, err := ValidateAWSProxy("https://www.example.com:1234")
Expect(err).To(HaveOccurred())
})
})

Context("Vault Address", func() {
It("should validate the correct vault address", func() {
vaultURL, err := ValidateVaultAddress("https://vault.dev.net/")
Expect(err).To(BeNil())
Expect(vaultURL).To(Equal("https://vault.dev.net/"))
})

It("should fail invalid vault address", func() {
_, err := ValidateVaultAddress("http://vault.dev.net/")
Expect(err).To(HaveOccurred())
})
})

Context("Vault Path", func() {
It("should validate the correct vault path", func() {
proxyURL, err := ValidateDtVaultPath("osd-sre/dynatrace/sd-sre-grail-logs")
Expect(err).To(BeNil())
Expect(proxyURL).To(Equal("osd-sre/dynatrace/sd-sre-grail-logs"))
})

It("should fail invalid vault path", func() {
_, err := ValidateDtVaultPath("/osd-sre/dynatrace/sd-sre-grail-logs/logs")
Expect(err).ShouldNot(HaveOccurred())
})
})
})

0 comments on commit 8350504

Please sign in to comment.