Skip to content

Commit

Permalink
Merge pull request #4 from guirava/master
Browse files Browse the repository at this point in the history
operation name when using ConnectServiceAccount functions
  • Loading branch information
guirava authored Jun 3, 2022
2 parents 62d17a4 + 240b96c commit 186d251
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 44 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# IDEs
.idea

# Binaries for programs and plugins
*.exe
*.exe~
Expand Down
100 changes: 57 additions & 43 deletions rubrikpolaris/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,18 @@ const (
encodePath encoding = 1 + iota
encodePathSegment
encodeQueryComponent
defaultServiceAccountFile = "~/.rubrik/polaris-service-account.json"
DefaultServiceAccountFile = "~/.rubrik/polaris-service-account.json"
)

// Credentials contains the parameters used to authenticate against the Rubrik
// cluster and can be populated through the ConnectX() factory functions:
// - Connect(),
// - ConnectEnv(),
// - ConnectServiceAccount() and
// - ConnectServiceAccountFromFile() and
// - ConnectServiceAccountFromString()
type Credentials struct {
PolarisDomain string
Host string
Username string
Password string
OperationName string
Expand All @@ -76,21 +77,17 @@ type apiToken struct {
// alternative. The operationName is an optional value which can be used to add
// a custom prefix to the GraphQL Operation Name which is useful for tracking
// specific usage in the Polaris logs.
func Connect(nodeIP, username, password string, operationName ...string) *Credentials {

operationNamePrefiex := "SdkGoLang"
if len(operationName) > 0 {
operationNamePrefiex = fmt.Sprintf("%s%s",
operationNamePrefiex, operationName[0])
}
func Connect(
nodeIP, username, password string,
operationName ...string) *Credentials {

client := &Credentials{
PolarisDomain: nodeIP,
Host: fmt.Sprintf("%s.my.rubrik.com", nodeIP),
Username: username,
Password: password,
OperationName: operationNamePrefiex,
OperationName: OperationNamePrefix(operationName...),
}

return client
}

Expand All @@ -105,7 +102,8 @@ func Connect(nodeIP, username, password string, operationName ...string) *Creden
//
// rubrik_cdm_token will always take precedence over rubrik_polaris_username
// and rubrik_polaris_password
func ConnectEnv() (*Credentials, error) {
func ConnectEnv(
operationName ...string) (*Credentials, error) {

polarisDomain, ok := os.LookupEnv("rubrik_polaris_domain")
if ok != true {
Expand All @@ -128,21 +126,41 @@ func ConnectEnv() (*Credentials, error) {

client = &Credentials{
PolarisDomain: polarisDomain,
Host: fmt.Sprintf("%s.my.rubrik.com", polarisDomain),
Username: username,
Password: password,
OperationName: OperationNamePrefix(operationName...),
}

return client, nil
}

// ConnectServiceAccount is similar to Connect but retrieves secrets from
// a service account JSON file. If args[0] is not given or if it is empty,
// defaultServiceAccountFile is used instead.
func ConnectServiceAccount(args ...string) (*Credentials, error) {
// ConnectServiceAccount is deprecated. It is equivalent to:
// ConnectServiceAccountFromFile(DefaultServiceAccountFile)
//
// Use ConnectServiceAccountFromFile or ConnectServiceAccountFromString instead.
// @deprecated
func ConnectServiceAccount(
args ...string) (*Credentials, error) {
if len(args) > 1 {
return nil, errors.New(
"too many arguments given to ConnectServiceAccount")
}
serviceAccountFile := GetStringFromSlice(args, 0, true,
DefaultServiceAccountFile)
return ConnectServiceAccountFromFile(serviceAccountFile, "")
}

// ConnectServiceAccountFromFile is similar to Connect but retrieves secrets
// from a service account JSON file.
//
// Example: use the default service account file:
//
// creds,err := ConnectServiceAccountFromFile(DefaultServiceAccountFile)
//
func ConnectServiceAccountFromFile(
serviceAccountFile string,
operationName ...string) (*Credentials, error) {

// UserAccount holds a Polaris local user account configuration.
type ServiceAccountFile struct {
Expand All @@ -161,9 +179,7 @@ func ConnectServiceAccount(args ...string) (*Credentials, error) {

var err error

// Determine what service account file to use:
serviceAccountFile := GetStringFromSlice(args, 0, true,
defaultServiceAccountFile)
// Resolve service account file path if needed:
serviceAccountFile, err = ExpandTildePath(serviceAccountFile)
if err != nil {
return nil, err
Expand All @@ -178,14 +194,16 @@ func ConnectServiceAccount(args ...string) (*Credentials, error) {
serviceAccountFile)
}

return ConnectServiceAccountFromString(buf)
return ConnectServiceAccountFromString(string(buf), operationName...)
}

// ConnectServiceAccountFromString is similar to ConnectServiceAccount
// ConnectServiceAccountFromString is similar to ConnectServiceAccountFromFile
// but takes the JSON string as parameter instead of the JSON file path.
func ConnectServiceAccountFromString(jsonString []byte) (*Credentials, error) {
func ConnectServiceAccountFromString(
jsonString string,
operationName ...string) (*Credentials, error) {
var accounts map[string]string
if err := json.Unmarshal(jsonString, &accounts); err != nil {
if err := json.Unmarshal([]byte(jsonString), &accounts); err != nil {
return nil, fmt.Errorf("invalid JSON string: %v", err)
}

Expand All @@ -211,14 +229,16 @@ func ConnectServiceAccountFromString(jsonString []byte) (*Credentials, error) {
missingServiceAccount)
}

polarisDomainSplit := strings.Split(accounts["access_token_uri"], "//")[1]
polarisDomain := strings.Split(polarisDomainSplit, ".")[0]
hostSplit := strings.Split(accounts["access_token_uri"], "//")[1]
host := strings.Split(hostSplit, "/")[0]

client := &Credentials{
PolarisDomain: polarisDomain,
PolarisDomain: strings.Split(host, ".")[0],
Host: host,
ClientId: accounts["client_id"],
ClientSecret: accounts["client_secret"],
AccessTokenUri: accounts["access_token_uri"],
OperationName: OperationNamePrefix(operationName...),
}

return client, nil
Expand All @@ -239,42 +259,36 @@ func (c *Credentials) commonAPI(
}

var requestURL string
if callType == "graphql" {
switch callType {

requestURL = fmt.Sprintf("https://%s.my.rubrik.com/api/graphql",
c.PolarisDomain)
case "serviceAccount":
requestURL = c.AccessTokenUri

case "graphql":
requestURL = fmt.Sprintf("https://%s/api/graphql", c.Host)
// Parse the Operation Name of the static GraphQL query

var staticOperationName string
if config["query"] == nil {
staticOperationName = parseOperationName(config["mutation"].(string))
} else {

staticOperationName = parseOperationName(config["query"].(string))

}

// Combine the predefined Operation Name with the Operation Name defined
// Combine the Operation Name prefix with the Operation Name defined
// in the static GQL query
config["operationName"] = fmt.Sprintf("%s%s",
c.OperationName, staticOperationName)
// Replace the Operation Name in the static GQL query with the new custom
// name
config["operationName"] = c.OperationName + staticOperationName
// Replace Operation Name in the static GQL query with new custom name
if config["query"] == nil {
config["query"] = strings.Replace(config["mutation"].(string),
staticOperationName, config["operationName"].(string), 1)
} else {
config["query"] = strings.Replace(config["query"].(string),
staticOperationName, config["operationName"].(string), 1)

}

} else if callType == "serviceAccount" {
requestURL = c.AccessTokenUri

} else {
requestURL = fmt.Sprintf("https://%s.my.rubrik.com/api/session",
c.PolarisDomain)
default:
requestURL = fmt.Sprintf("https://%s/api/session", c.Host)

}

Expand Down
25 changes: 24 additions & 1 deletion rubrikpolaris/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
"strings"
)

const (
operationNamePrefix = "SdkGoLang"
)

// ExpandTildePath will replace a "~/" prefix with the user home directory.
// Caveat: only "~/" is supported (~user/path is not).
func ExpandTildePath(path string) (string, error) {
Expand All @@ -20,7 +24,7 @@ func ExpandTildePath(path string) (string, error) {
return path, nil
}

// GetStringFromSlice is a workaround for Go's lack of default func params:
// GetStringFromSlice is useful to emulate default function params:
// it returns s[index] if it exists; if it doesn't, it returns defaultVal;
// if replaceEmpty is true and s[index] is empty, it also returns defaultVal.
func GetStringFromSlice(s []string, index int, replaceEmpty bool,
Expand All @@ -30,3 +34,22 @@ func GetStringFromSlice(s []string, index int, replaceEmpty bool,
}
return defaultVal
}

// OperationNamePrefix returns a prefix string to be used when sending up
// operation names to Rubrik. The default is "SdkGoLang".
// So for instance if sending up the query "RadarEventsPerTimePeriod",
// the operation name will be "SdkGoLangRadarEventsPerTimePeriod".
//
// A less common use case is to specify a *second* prefix: say
// operationNameSecondPrefix="XYZ", then for the query above, the operation
// name sent up to Rubrik would be "SdkGoLangXYZRadarEventsPerTimePeriod"
func OperationNamePrefix(operationNameSecondPrefix ...string) string {
if len(operationNameSecondPrefix) == 0 {
return operationNamePrefix
}
op := operationNameSecondPrefix[0]
if !strings.HasPrefix(op, operationNamePrefix) {
return operationNamePrefix + op
}
return op
}

0 comments on commit 186d251

Please sign in to comment.