From 7d6184e2f2b1f3e3414909919430a1f9dcc6a385 Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Mon, 2 Sep 2024 14:42:54 -0700 Subject: [PATCH] add: `get sagemaker-domain` --- cmd/get/get.go | 3 + cmd/get/sagemaker.go | 32 +++++ go.mod | 9 +- go.sum | 10 ++ pkg/aws/sagemaker.go | 39 ++++++ pkg/resource/sagemaker/domain/get.go | 114 ++++++++++++++++++ pkg/resource/sagemaker/domain/options.go | 41 +++++++ pkg/resource/sagemaker/domain/printer.go | 47 ++++++++ .../sagemaker/domain/sagemaker_domain.go | 24 ++++ 9 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 cmd/get/sagemaker.go create mode 100644 pkg/aws/sagemaker.go create mode 100644 pkg/resource/sagemaker/domain/get.go create mode 100644 pkg/resource/sagemaker/domain/options.go create mode 100644 pkg/resource/sagemaker/domain/printer.go create mode 100644 pkg/resource/sagemaker/domain/sagemaker_domain.go diff --git a/cmd/get/get.go b/cmd/get/get.go index 2ef9614..141958e 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -121,6 +121,9 @@ func NewGetCmd() *cobra.Command { cmd.AddCommand(prefix_list.NewResource().NewGetCmd()) cmd.AddCommand(route_table.NewResource().NewGetCmd()) cmd.AddCommand(s3_bucket.NewResource().NewGetCmd()) + cmd.AddCommand(NewGetSageMakerCmd()) + cmd.AddCommand(NewGetAliasCmds(sagemaker, "sagemaker-")...) + cmd.AddCommand(NewGetAliasCmds(sagemaker, "sm-")...) cmd.AddCommand(security_group.NewResource().NewGetCmd()) cmd.AddCommand(security_group_rule.NewResource().NewGetCmd()) cmd.AddCommand(sqs_queue.NewResource().NewGetCmd()) diff --git a/cmd/get/sagemaker.go b/cmd/get/sagemaker.go new file mode 100644 index 0000000..cc80928 --- /dev/null +++ b/cmd/get/sagemaker.go @@ -0,0 +1,32 @@ +package get + +import ( + "github.com/awslabs/eksdemo/pkg/resource" + "github.com/awslabs/eksdemo/pkg/resource/sagemaker/domain" + "github.com/spf13/cobra" +) + +var sagemaker []func() *resource.Resource + +func NewGetSageMakerCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "sagemaker", + Short: "Amazon SageMaker Resources", + Aliases: []string{"sm"}, + } + + // Don't show flag errors for `get sagemaker` without a subcommand + cmd.DisableFlagParsing = true + + for _, r := range sagemaker { + cmd.AddCommand(r().NewGetCmd()) + } + + return cmd +} + +func init() { + sagemaker = []func() *resource.Resource{ + domain.New, + } +} diff --git a/go.mod b/go.mod index 327ffe7..3a3f9bf 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.21.10 require ( github.com/Masterminds/goutils v1.1.1 github.com/Masterminds/sprig/v3 v3.2.3 - github.com/aws/aws-sdk-go-v2 v1.25.2 + github.com/aws/aws-sdk-go-v2 v1.30.4 github.com/aws/aws-sdk-go-v2/config v1.27.4 github.com/aws/aws-sdk-go-v2/service/acm v1.25.1 github.com/aws/aws-sdk-go-v2/service/amp v1.25.1 @@ -34,7 +34,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 github.com/aws/aws-sdk-go-v2/service/vpclattice v1.7.1 github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c - github.com/aws/smithy-go v1.20.1 + github.com/aws/smithy-go v1.20.4 github.com/go-resty/resty/v2 v2.7.0 github.com/google/uuid v1.3.0 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b @@ -68,14 +68,15 @@ require ( github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.4 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sagemaker v1.154.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index a051a06..73cc97f 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/aws/aws-sdk-go v1.34.9 h1:cUGBW9CVdi0mS7K1hDzxIqTpfeWhpoQiguq81M1tjK0 github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go-v2 v1.25.2 h1:/uiG1avJRgLGiQM9X3qJM8+Qa6KRGK5rRPuXE0HUM+w= github.com/aws/aws-sdk-go-v2 v1.25.2/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo= +github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= +github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo= github.com/aws/aws-sdk-go-v2/config v1.27.4 h1:AhfWb5ZwimdsYTgP7Od8E9L1u4sKmDW2ZVeLcf2O42M= @@ -83,8 +85,12 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 h1:AK0J8iYBFeUk2Ax7O8YpLtF github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2/go.mod h1:iRlGzMix0SExQEviAyptRWRGdYNo3+ufW/lCzvKVTUc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 h1:bNo4LagzUKbjdxE0tIcR9pMzLR2U/Tgie1Hq1HQ3iH8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2/go.mod h1:wRQv0nN6v9wDXuWThpovGQjqF1HFdcgWjporw14lS8k= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 h1:EtOU5jsPdIQNP+6Q2C5e3d65NKT1PeCiQk+9OdzO12Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2/go.mod h1:tyF5sKccmDz0Bv4NrstEr+/9YkSPJHrcO7UsUKf7pWM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2 h1:en92G0Z7xlksoOylkUhuBSfJgijC7rHVLRdnIlHEs0E= @@ -137,6 +143,8 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.40.1 h1:NRKxGOS+FKUA84EfbgkLCleB github.com/aws/aws-sdk-go-v2/service/route53 v1.40.1/go.mod h1:7Wa9sIDxey/5b2FK5r1Z6ryVfojt4Nl+VzzpK8q1L+M= github.com/aws/aws-sdk-go-v2/service/s3 v1.51.1 h1:juZ+uGargZOrQGNxkVHr9HHR/0N+Yu8uekQnV7EAVRs= github.com/aws/aws-sdk-go-v2/service/s3 v1.51.1/go.mod h1:SoR0c7Jnq8Tpmt0KSLXIavhjmaagRqQpe9r70W3POJg= +github.com/aws/aws-sdk-go-v2/service/sagemaker v1.154.0 h1:NDEbY45I7YFiSAW055YdE6fFoxmudl+jK/8qe//Bduk= +github.com/aws/aws-sdk-go-v2/service/sagemaker v1.154.0/go.mod h1:tn9CZCzeX7NC+qhWtnsN7GUzXG64/QUqjxeZZetzjpo= github.com/aws/aws-sdk-go-v2/service/sqs v1.31.1 h1:124rVNP6NbCfBZwiX1kfjMQrnsJtnpKeB0GalkuqSXo= github.com/aws/aws-sdk-go-v2/service/sqs v1.31.1/go.mod h1:YijRvM1SAmuiIQ9pjfwahIEE3HMHUkx9P5oplL/Jnj4= github.com/aws/aws-sdk-go-v2/service/ssm v1.49.1 h1:MeYuN4Ld4FWVJb9ZiOJkon7/foj0Zm2GTDorSaInHj4= @@ -153,6 +161,8 @@ github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c h1:6cCr github.com/aws/session-manager-plugin v0.0.0-20221012155945-c523002ee02c/go.mod h1:7n17tunRPUsniNBu5Ja9C7WwJWTdOzaLqr/H0Ns3uuI= github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= +github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/pkg/aws/sagemaker.go b/pkg/aws/sagemaker.go new file mode 100644 index 0000000..93f6b27 --- /dev/null +++ b/pkg/aws/sagemaker.go @@ -0,0 +1,39 @@ +package aws + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/aws/aws-sdk-go-v2/service/sagemaker/types" +) + +type SageMakerClient struct { + *sagemaker.Client +} + +func NewSageMakerClient() *SageMakerClient { + return &SageMakerClient{sagemaker.NewFromConfig(GetConfig())} +} + +func (c *SageMakerClient) DescribeDomain(id string) (*sagemaker.DescribeDomainOutput, error) { + result, err := c.Client.DescribeDomain(context.Background(), &sagemaker.DescribeDomainInput{ + DomainId: aws.String(id), + }) + + if err != nil { + return nil, err + } + + return result, nil +} + +func (c *SageMakerClient) ListDomains() ([]types.DomainDetails, error) { + result, err := c.Client.ListDomains(context.Background(), &sagemaker.ListDomainsInput{}) + + if err != nil { + return nil, err + } + + return result.Domains, nil +} diff --git a/pkg/resource/sagemaker/domain/get.go b/pkg/resource/sagemaker/domain/get.go new file mode 100644 index 0000000..71c1e00 --- /dev/null +++ b/pkg/resource/sagemaker/domain/get.go @@ -0,0 +1,114 @@ +package domain + +import ( + "fmt" + "os" + "strings" + + awssdk "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/aws/aws-sdk-go-v2/service/sagemaker/types" + "github.com/awslabs/eksdemo/pkg/aws" + "github.com/awslabs/eksdemo/pkg/printer" + "github.com/awslabs/eksdemo/pkg/resource" +) + +type Getter struct { + sagemakerClient *aws.SageMakerClient +} + +func NewGetter(sagemakerClient *aws.SageMakerClient) *Getter { + return &Getter{sagemakerClient} +} + +func (g *Getter) Init() { + if g.sagemakerClient == nil { + g.sagemakerClient = aws.NewSageMakerClient() + } +} + +func (g *Getter) Get(domainName string, output printer.Output, o resource.Options) error { + options, ok := o.(*Options) + if !ok { + return fmt.Errorf("internal error, unable to cast options to domain.Options") + } + + var domain *sagemaker.DescribeDomainOutput + var domains []*sagemaker.DescribeDomainOutput + var err error + + switch { + case domainName != "": + domain, err = g.GetDomainByName(domainName) + domains = []*sagemaker.DescribeDomainOutput{domain} + case options.DomainID != "": + domain, err = g.GetDomainByID(options.DomainID) + domains = []*sagemaker.DescribeDomainOutput{domain} + default: + domains, err = g.GetAllDomains() + } + + if err != nil { + return err + } + + return output.Print(os.Stdout, NewPrinter(domains)) +} + +func (g *Getter) GetAllDomains() ([]*sagemaker.DescribeDomainOutput, error) { + domainDetails, err := g.sagemakerClient.ListDomains() + if err != nil { + return nil, err + } + + domains := make([]*sagemaker.DescribeDomainOutput, 0, len(domainDetails)) + + for _, dd := range domainDetails { + result, err := g.sagemakerClient.DescribeDomain(awssdk.ToString(dd.DomainId)) + if err != nil { + return nil, err + } + domains = append(domains, result) + } + + return domains, nil +} + +func (g *Getter) GetDomainByID(domainId string) (*sagemaker.DescribeDomainOutput, error) { + domain, err := g.sagemakerClient.DescribeDomain(domainId) + if err != nil { + return nil, aws.FormatError(err) + } + + return domain, nil +} + +func (g *Getter) GetDomainByName(domainName string) (*sagemaker.DescribeDomainOutput, error) { + domainDetails, err := g.sagemakerClient.ListDomains() + if err != nil { + return nil, err + } + + found := []types.DomainDetails{} + + for _, dd := range domainDetails { + if strings.EqualFold(domainName, awssdk.ToString(dd.DomainName)) { + found = append(found, dd) + } + } + + if len(found) == 0 { + return nil, &resource.NotFoundByNameError{Type: "sagemaker-domain", Name: domainName} + } + + if len(found) > 1 { + return nil, fmt.Errorf("multiple sagemaker domains found with name: %s", domainName) + } + + domain, err := g.sagemakerClient.DescribeDomain(awssdk.ToString(found[0].DomainId)) + if err != nil { + return nil, err + } + + return domain, nil +} diff --git a/pkg/resource/sagemaker/domain/options.go b/pkg/resource/sagemaker/domain/options.go new file mode 100644 index 0000000..27d53db --- /dev/null +++ b/pkg/resource/sagemaker/domain/options.go @@ -0,0 +1,41 @@ +package domain + +import ( + "github.com/awslabs/eksdemo/pkg/cmd" + "github.com/awslabs/eksdemo/pkg/resource" + "github.com/spf13/cobra" +) + +type Options struct { + resource.CommonOptions + + // Get + DomainID string +} + +func newOptions() (options *Options, getFlags cmd.Flags) { + options = &Options{ + CommonOptions: resource.CommonOptions{ + Name: "sagemaker-domain", + ClusterFlagDisabled: true, + }, + } + + getFlags = cmd.Flags{ + &cmd.StringFlag{ + CommandFlag: cmd.CommandFlag{ + Name: "id", + Description: "get by id instead of name", + Validate: func(_ *cobra.Command, args []string) error { + if options.DomainID != "" && len(args) > 0 { + return &cmd.ArgumentAndFlagCantBeUsedTogetherError{Arg: "DOMAIN_NAME", Flag: "--id"} + } + return nil + }, + }, + Option: &options.DomainID, + }, + } + + return +} diff --git a/pkg/resource/sagemaker/domain/printer.go b/pkg/resource/sagemaker/domain/printer.go new file mode 100644 index 0000000..35d0b5e --- /dev/null +++ b/pkg/resource/sagemaker/domain/printer.go @@ -0,0 +1,47 @@ +package domain + +import ( + "io" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/sagemaker" + "github.com/awslabs/eksdemo/pkg/printer" + "github.com/hako/durafmt" +) + +type Printer struct { + domains []*sagemaker.DescribeDomainOutput +} + +func NewPrinter(domains []*sagemaker.DescribeDomainOutput) *Printer { + return &Printer{domains} +} + +func (p *Printer) PrintTable(writer io.Writer) error { + table := printer.NewTablePrinter() + table.SetHeader([]string{"Age", "Status", "Id", "Domain"}) + + for _, d := range p.domains { + age := durafmt.ParseShort(time.Since(aws.ToTime(d.CreationTime))) + + table.AppendRow([]string{ + age.String(), + string(d.Status), + aws.ToString(d.DomainId), + aws.ToString(d.DomainName), + }) + } + + table.Print(writer) + + return nil +} + +func (p *Printer) PrintJSON(writer io.Writer) error { + return printer.EncodeJSON(writer, p.domains) +} + +func (p *Printer) PrintYAML(writer io.Writer) error { + return printer.EncodeYAML(writer, p.domains) +} diff --git a/pkg/resource/sagemaker/domain/sagemaker_domain.go b/pkg/resource/sagemaker/domain/sagemaker_domain.go new file mode 100644 index 0000000..8e0bb25 --- /dev/null +++ b/pkg/resource/sagemaker/domain/sagemaker_domain.go @@ -0,0 +1,24 @@ +package domain + +import ( + "github.com/awslabs/eksdemo/pkg/cmd" + "github.com/awslabs/eksdemo/pkg/resource" +) + +func New() *resource.Resource { + options, getFlags := newOptions() + + return &resource.Resource{ + Command: cmd.Command{ + Name: "domain", + Description: "SageMaker Domain", + Args: []string{"DOMAIN_NAME"}, + }, + + GetFlags: getFlags, + + Getter: &Getter{}, + + Options: options, + } +}