Skip to content

Commit

Permalink
Merge pull request #126 from astronomerio/feature/service-accounts
Browse files Browse the repository at this point in the history
Feature/service accounts
  • Loading branch information
andscoop committed Sep 21, 2018
2 parents 84ae936 + 42e6d1d commit 22919f0
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 30 deletions.
140 changes: 140 additions & 0 deletions cmd/serviceaccount.go
@@ -0,0 +1,140 @@
package cmd

import (
sa "github.com/astronomerio/astro-cli/serviceaccount"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

var (
workspaceUuid string
deploymentUuid string
userUuid string
systemSA bool
category string
label string

saRootCmd = &cobra.Command{
Use: "service-account",
Aliases: []string{"sa"},
Short: "Manage astronomer service accounts",
Long: "Service-accounts represent a revokable token with access to the Astronomer platform",
}

saCreateCmd = &cobra.Command{
Use: "create",
Aliases: []string{"cr"},
Short: "Create a service-account in the astronomer platform",
Long: "Create a service-account in the astronomer platform",
RunE: saCreate,
}

saDeleteCmd = &cobra.Command{
Use: "delete [SA-UUID]",
Aliases: []string{"de"},
Short: "Delete a service-account in the astronomer platform",
Long: "Delete a service-account in the astronomer platform",
RunE: saDelete,
Args: cobra.ExactArgs(1),
}

saGetCmd = &cobra.Command{
Use: "get",
Short: "Get a service-account by entity type and entity uuid",
Long: "Get a service-account by entity type and entity uuid",
RunE: saGet,
}
)

func init() {
// User root
RootCmd.AddCommand(saRootCmd)

// Service-account create
saRootCmd.AddCommand(saCreateCmd)
saCreateCmd.Flags().StringVarP(&workspaceUuid, "workspace-uuid", "w", "", "[UUID]")
saCreateCmd.Flags().StringVarP(&deploymentUuid, "deployment-uuid", "d", "", "[UUID]")
saCreateCmd.Flags().StringVarP(&userUuid, "user-uuid", "u", "", "[UUID]")
saCreateCmd.Flags().BoolVarP(&systemSA, "system-sa", "s", false, "")
saCreateCmd.Flags().StringVarP(&category, "category", "c", "default", "CATEGORY")
saCreateCmd.Flags().StringVarP(&label, "label", "l", "", "LABEL")

saRootCmd.AddCommand(saGetCmd)
saGetCmd.Flags().StringVarP(&workspaceUuid, "workspace-uuid", "w", "", "[UUID]")
saGetCmd.Flags().StringVarP(&deploymentUuid, "deployment-uuid", "d", "", "[UUID]")
saGetCmd.Flags().StringVarP(&userUuid, "user-uuid", "u", "", "[UUID]")
saGetCmd.Flags().BoolVarP(&systemSA, "system-sa", "s", false, "")

saRootCmd.AddCommand(saDeleteCmd)
}

func getValidEntity() (string, string, error) {
var uuid string
var entityType string
singleArgVerify := 0

if systemSA {
entityType = "SYSTEM"
singleArgVerify++
}

if len(workspaceUuid) > 0 {
entityType = "WORKSPACE"
uuid = workspaceUuid
singleArgVerify++
}

if len(deploymentUuid) > 0 {
entityType = "DEPLOYMENT"
uuid = deploymentUuid
singleArgVerify++
}

if len(userUuid) > 0 {
entityType = "USER"
uuid = userUuid
singleArgVerify++
}

if singleArgVerify != 1 {
return "", "", errors.New("must specify exactly one service-account type (system, workspace deployment, user")
}

return entityType, uuid, nil
}

func saCreate(cmd *cobra.Command, args []string) error {
// Validation
entityType, uuid, err := getValidEntity()
if err != nil {
return err
}

if len(label) == 0 {
return errors.New("must provide a service-account label with the --label (-l) flag")
}

// Silence Usage as we have now validated command input
cmd.SilenceUsage = true

return sa.Create(uuid, label, category, entityType)
}

func saDelete(cmd *cobra.Command, args []string) error {
// Silence Usage as we have now validated command input
cmd.SilenceUsage = true

return sa.Delete(args[0])
}

func saGet(cmd *cobra.Command, args []string) error {
entityType, uuid, err := getValidEntity()
if err != nil {
return err
}

// Silence Usage as we have now validated command input
cmd.SilenceUsage = true

return sa.Get(entityType, uuid)
}
2 changes: 1 addition & 1 deletion deployment/deployment.go
Expand Up @@ -57,7 +57,7 @@ func List(ws string, all bool) error {
var deployments []houston.Deployment
var err error

r := " %-30s %-50s %-30s %-50s"
r := " %-30s %-50s %-50s %-50s"
h := fmt.Sprintf(r, "NAME", "RELEASE NAME", "DEPLOYMENT ID", "WORKSPACE")
// colorFmt := "\033[33;m"
// colorTrm := "\033[0m"
Expand Down
120 changes: 106 additions & 14 deletions houston/houston.go
Expand Up @@ -123,6 +123,59 @@ var (
}
}`

serviceAccountCreateRequest = `
mutation CreateServiceAccount {
createServiceAccount(
entityUuid: "%s",
label: "%s",
category: "%s",
entityType: %s
) {
uuid
apiKey
label
category
entityType
entityUuid
active
createdAt
updatedAt
lastUsedAt
}
}`

serviceAccountDeleteRequest = `
mutation DeleteServiceAccount {
deleteServiceAccount(
serviceAccountUuid:"%s"
) {
uuid
label
category
entityType
entityUuid
}
}`

serviceAccountsGetRequest = `
query GetServiceAccount {
serviceAccounts(
entityType:%s,
entityUuid:"%s"
) {
uuid
apiKey
label
category
entityType
entityUuid
active
createdAt
updatedAt
lastUsedAt
}
}`

tokenBasicCreateRequest = `
mutation createBasicToken {
createToken(
Expand Down Expand Up @@ -388,6 +441,19 @@ func (c *Client) CreateBasicToken(email, password string) (*AuthUser, error) {
return response.Data.CreateToken, nil
}

// CreateServiceAccount sends a request to Houston in order to fetch a newly created service account
// Returns a ServiceAccount object
func (c *Client) CreateServiceAccount(entityUuid, label, category, entityType string) (*ServiceAccount, error) {
request := fmt.Sprintf(serviceAccountCreateRequest, entityUuid, label, category, entityType)

response, err := c.QueryHouston(request)
if err != nil {
return nil, errors.Wrap(err, "CreateServiceAccount Failed")
}

return response.Data.CreateServiceAccount, nil
}

// CreateUser sends request to request to Houston in order to create a new platform User
// Returns an AuthUser object containing an token
func (c *Client) CreateUser(email string, password string) (*AuthUser, error) {
Expand Down Expand Up @@ -426,7 +492,7 @@ func (c *Client) DeleteDeployment(uuid string) (*Deployment, error) {
return response.Data.DeleteDeployment, nil
}

// DeleteWorkspace will send a request to Houston to create a new workspace
// DeleteWorkspace will send a request to Houston to delete a workspace
// Returns an object representing deleted workspace
func (c *Client) DeleteWorkspace(uuid string) (*Workspace, error) {
request := fmt.Sprintf(workspaceDeleteRequest, uuid)
Expand All @@ -452,6 +518,19 @@ func (c *Client) GetAllDeployments() ([]Deployment, error) {
return response.Data.GetDeployments, nil
}

// DeleteServiceAccount will send a request to Houston to delete a service account
// Returns an object representing deleted service account
func (c *Client) DeleteServiceAccount(uuid string) (*ServiceAccount, error) {
request := fmt.Sprintf(serviceAccountDeleteRequest, uuid)

response, err := c.QueryHouston(request)
if err != nil {
return nil, errors.Wrap(err, "DeleteServiceAccount Request")
}

return response.Data.DeleteServiceAccount, nil
}

// GetDeployments will request all airflow deployments from Houston
// Returns a []Deployment structure with deployment details
func (c *Client) GetDeployments(ws string) ([]Deployment, error) {
Expand All @@ -467,19 +546,19 @@ func (c *Client) GetDeployments(ws string) ([]Deployment, error) {

// GetDeployment will request a specific airflow deployments from Houston by uuid
// Returns a Deployment structure with deployment details
func (c *Client) GetDeployment(deploymentUuid string) (*Deployment, error) {
request := fmt.Sprintf(deploymentGetRequest, deploymentUuid)
// func (c *Client) GetDeployment(deploymentUuid string) (*Deployment, error) {
// request := fmt.Sprintf(deploymentGetRequest, deploymentUuid)

response, err := c.QueryHouston(request)
if err != nil {
return nil, errors.Wrap(err, "GetDeployment Failed")
}
// response, err := c.QueryHouston(request)
// if err != nil {
// return nil, errors.Wrap(err, "GetDeployment Failed")
// }

if len(response.Data.GetDeployments) == 0 {
return nil, fmt.Errorf("deployment not found for uuid \"%s\"", deploymentUuid)
}
return &response.Data.GetDeployments[0], nil
}
// if len(response.Data.GetDeployments) == 0 {
// return nil, fmt.Errorf("deployment not found for uuid \"%s\"", deploymentUuid)
// }
// return &response.Data.GetDeployments[0], nil
// }

// GetAuthConfig will get authentication configuration from houston
// Returns the requested AuthConfig object
Expand All @@ -494,8 +573,21 @@ func (c *Client) GetAuthConfig() (*AuthConfig, error) {
return response.Data.GetAuthConfig, nil
}

// GetWorkspaceAll returns all available workspaces from houston API
// Returns a slice of all Workspaces a user has access to
// GetServiceAccounts will get GetServiceAccounts from houston
// Returns slice of GetServiceAccounts
func (c *Client) GetServiceAccounts(entityType, uuid string) ([]ServiceAccount, error) {
request := fmt.Sprintf(serviceAccountsGetRequest, entityType, uuid)

response, err := c.QueryHouston(request)
if err != nil {
return nil, errors.Wrap(err, "GetServiceAccounts Failed")
}

return response.Data.GetServiceAccounts, nil
}

// GetWorkspace returns all available workspaces from houston API
// Returns a workspace struct
func (c *Client) GetWorkspace(uuid string) (*Workspace, error) {
request := fmt.Sprintf(workspaceGetRequest, uuid)

Expand Down
45 changes: 30 additions & 15 deletions houston/types.go
Expand Up @@ -3,21 +3,24 @@ package houston
// HoustonResponse wraps all houston response structs used for json marashalling
type HoustonResponse struct {
Data struct {
AddWorkspaceUser *Workspace `json:"workspaceAddUser,omitempty"`
RemoveWorkspaceUser *Workspace `json:"workspaceRemoveUser,omitempty"`
CreateDeployment *Deployment `json:"createDeployment,omitempty"`
CreateToken *AuthUser `json:"createToken,omitempty"`
CreateUser *AuthUser `json:"createUser,omitempty"`
CreateWorkspace *Workspace `json:"createWorkspace,omitempty"`
DeleteDeployment *Deployment `json:"deleteDeployment,omitempty"`
DeleteWorkspace *Workspace `json:"deleteWorkspace,omitempty"`
GetDeployments []Deployment `json:"deployments,omitempty"`
GetAuthConfig *AuthConfig `json:"authConfig,omitempty"`
GetUsers []User `json:"users,omitempty"`
GetWorkspace *Workspace `json:"workspace,omitempty"`
GetWorkspaces []Workspace `json:"workspaces,omitempty"`
UpdateDeployment *Deployment `json:"updateDeployment,omitempty"`
UpdateWorkspace *Workspace `json:"updateWorkspace,omitempty"`
AddWorkspaceUser *Workspace `json:"workspaceAddUser,omitempty"`
RemoveWorkspaceUser *Workspace `json:"workspaceRemoveUser,omitempty"`
CreateDeployment *Deployment `json:"createDeployment,omitempty"`
CreateToken *AuthUser `json:"createToken,omitempty"`
CreateServiceAccount *ServiceAccount `json:"createServiceAccount,omitempty"`
CreateUser *AuthUser `json:"createUser,omitempty"`
CreateWorkspace *Workspace `json:"createWorkspace,omitempty"`
DeleteDeployment *Deployment `json:"deleteDeployment,omitempty"`
DeleteServiceAccount *ServiceAccount `json:"deleteServiceAccount,omitempty"`
DeleteWorkspace *Workspace `json:"deleteWorkspace,omitempty"`
GetDeployments []Deployment `json:"deployments,omitempty"`
GetAuthConfig *AuthConfig `json:"authConfig,omitempty"`
GetServiceAccounts []ServiceAccount `json:"serviceAccounts,omitempty"`
GetUsers []User `json:"users,omitempty"`
GetWorkspace *Workspace `json:"workspace,omitempty"`
GetWorkspaces []Workspace `json:"workspaces,omitempty"`
UpdateDeployment *Deployment `json:"updateDeployment,omitempty"`
UpdateWorkspace *Workspace `json:"updateWorkspace,omitempty"`
} `json:"data"`
Errors []Error `json:"errors,omitempty"`
}
Expand Down Expand Up @@ -85,6 +88,18 @@ type Status struct {
Id string `json:"id"`
}

// ServiceACcount defines a structure of a ServiceAccountResponse object
type ServiceAccount struct {
Uuid string `json:"uuid"`
ApiKey string `json:"apiKey"`
Label string `json:"label"`
Category string `json:"category"`
EntityType string `json:"entityType"`
EntityUuid string `json:"entityUuid"`
LastUsedAt string `json:"lastUsedAt"`
Active bool `json:"active"`
}

// Token contains a houston auth token as well as it's payload of components
type Token struct {
Value string `json:"value"`
Expand Down

0 comments on commit 22919f0

Please sign in to comment.