diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc26f0b..d3dc54a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,5 @@ jobs: - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - - name: Checkout code - uses: actions/checkout@v4 - name: Test run: go test ./... \ No newline at end of file diff --git a/Makefile b/Makefile index 309e55c..8af8ae8 100644 --- a/Makefile +++ b/Makefile @@ -10,8 +10,10 @@ generate: deps mockgen --destination ./internal/mocks/collection.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/collection.go mockgen --destination ./internal/mocks/hosts_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/hosts.go mockgen --destination ./internal/mocks/ssh_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssh_keys.go + mockgen --destination ./internal/mocks/ssl_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssl_certificates.go sed -i '' 's|github.com/serverscom/srvctl/vendor/github.com/serverscom/serverscom-go-client/pkg|github.com/serverscom/serverscom-go-client/pkg|g' \ ./internal/mocks/ssh_service.go \ ./internal/mocks/hosts_service.go \ + ./internal/mocks/ssl_service.go \ ./internal/mocks/collection.go diff --git a/cmd/base/hooks.go b/cmd/base/hooks.go index 90f1bfd..61f390d 100644 --- a/cmd/base/hooks.go +++ b/cmd/base/hooks.go @@ -80,6 +80,9 @@ func CheckFormatterFlags(cmdContext *CmdContext, entities map[string]entities.En if entity == nil { return fmt.Errorf("can't find entity") } + if err := entity.SetCmdDefaultFields(cmd.Name()); err != nil { + return err + } if fieldList { formatter.ListEntityFields(entity.GetFields()) os.Exit(0) diff --git a/cmd/base/list.go b/cmd/base/list.go index f15bcfb..2974491 100644 --- a/cmd/base/list.go +++ b/cmd/base/list.go @@ -1,6 +1,7 @@ package base import ( + "log" "strings" serverscom "github.com/serverscom/serverscom-go-client/pkg" @@ -10,9 +11,16 @@ import ( type ListOptions[T any] interface { AddFlags(*cobra.Command) ApplyToCollection(serverscom.Collection[T]) +} + +type AllPager interface { AllPages() bool } +func NewListOptions[T any](opts ...ListOptions[T]) []ListOptions[T] { + return opts +} + // CollectionFactory is a function type that creates a typed resource collection // with configurable verbosity level // type CollectionFactory[T any] func(verbose bool) serverscom.Collection[T] @@ -35,6 +43,11 @@ func (o *BaseListOptions[T]) AddFlags(cmd *cobra.Command) { flags.StringVar(&o.sorting, "sorting", "", "Sort field") flags.StringVar(&o.direction, "direction", "", "Sort direction (ASC or DESC)") flags.BoolVarP(&o.allPages, "all", "A", false, "Get all pages of resources") + + flags.String("type", "", "") + if err := flags.MarkHidden("type"); err != nil { + log.Fatal(err) + } } // ApplyToCollection applies the options to a collection @@ -59,28 +72,36 @@ func (o *BaseListOptions[T]) AllPages() bool { return o.allPages } -// BaseLabelsListOptions is a base options struct for list commands with label selector option -type BaseLabelsListOptions[T any] struct { - BaseListOptions[T] +type LabelSelectorOption[T any] struct { labelSelector string } -// AddFlags adds common list flags to the command -func (o *BaseLabelsListOptions[T]) AddFlags(cmd *cobra.Command) { - o.BaseListOptions.AddFlags(cmd) - flags := cmd.Flags() - flags.StringVar(&o.labelSelector, "label-selector", "", "Filter by label selector") +func (o *LabelSelectorOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.labelSelector, "label-selector", "", "Filter results by labels") } -// ApplyToCollection applies the options to a collection -func (o *BaseLabelsListOptions[T]) ApplyToCollection(collection serverscom.Collection[T]) { +func (o *LabelSelectorOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { if o.labelSelector != "" { collection.SetParam("label_selector", o.labelSelector) } } +type SearchPatternOption[T any] struct { + searchPattern string +} + +func (o *SearchPatternOption[T]) AddFlags(cmd *cobra.Command) { + cmd.Flags().StringVar(&o.searchPattern, "search-pattern", "", "Return resources containing the parameter value in its name") +} + +func (o *SearchPatternOption[T]) ApplyToCollection(collection serverscom.Collection[T]) { + if o.searchPattern != "" { + collection.SetParam("search_pattern", o.searchPattern) + } +} + // NewListCmd base list command for different collections -func NewListCmd[T any](use string, entityName string, colFactory CollectionFactory[T], cmdContext *CmdContext, opts ListOptions[T]) *cobra.Command { +func NewListCmd[T any](use string, entityName string, colFactory CollectionFactory[T], cmdContext *CmdContext, opts ...ListOptions[T]) *cobra.Command { aliases := []string{} if use == "list" { aliases = append(aliases, "ls") @@ -99,16 +120,11 @@ func NewListCmd[T any](use string, entityName string, colFactory CollectionFacto SetupProxy(cmd, manager) collection := colFactory(manager.GetVerbose(cmd), args...) - opts.ApplyToCollection(collection) - - var items []T - var err error - if opts.AllPages() { - items, err = collection.Collect(ctx) - } else { - items, err = collection.List(ctx) + for _, opt := range opts { + opt.ApplyToCollection(collection) } + items, err := fetchItems(ctx, collection, opts) if err != nil { return err } @@ -118,7 +134,9 @@ func NewListCmd[T any](use string, entityName string, colFactory CollectionFacto }, } - opts.AddFlags(cmd) + for _, opt := range opts { + opt.AddFlags(cmd) + } return cmd } diff --git a/cmd/base/utils.go b/cmd/base/utils.go index 8083cfc..e26a5c5 100644 --- a/cmd/base/utils.go +++ b/cmd/base/utils.go @@ -11,6 +11,7 @@ import ( "strings" "time" + serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/internal/client" "github.com/serverscom/srvctl/internal/config" "github.com/serverscom/srvctl/internal/output" @@ -183,3 +184,13 @@ func NoArgs(cmd *cobra.Command, args []string) error { } return nil } + +func fetchItems[T any](ctx context.Context, collection serverscom.Collection[T], opts []ListOptions[T]) ([]T, error) { + for _, opt := range opts { + if baseOpts, ok := opt.(AllPager); ok && baseOpts.AllPages() { + return collection.Collect(ctx) + } + } + + return collection.List(ctx) +} diff --git a/cmd/entities/hosts/hosts.go b/cmd/entities/hosts/hosts.go index 0d7ebbf..2a1224c 100644 --- a/cmd/entities/hosts/hosts.go +++ b/cmd/entities/hosts/hosts.go @@ -19,8 +19,9 @@ type HostTypeCmd struct { } type HostManagers struct { - getMgr HostGetter - createMgr HostCreator + getMgr HostGetter + createMgr HostCreator + // for update we use simple commands in sake of simplicity powerMgr HostPowerer reinstallMgr HostReinstaller } @@ -119,7 +120,7 @@ func newHostTypeCmd(cmdContext *base.CmdContext, hostTypeCmd HostTypeCmd) *cobra hostCmd.AddCommand(newListCmd(cmdContext, &hostTypeCmd)) hostCmd.AddCommand(newGetCmd(cmdContext, &hostTypeCmd)) - if hostTypeCmd.managers.getMgr != nil { + if hostTypeCmd.managers.createMgr != nil { hostCmd.AddCommand(newAddCmd(cmdContext, &hostTypeCmd)) } if hostTypeCmd.managers.powerMgr != nil { @@ -144,6 +145,7 @@ func getHostsEntities() (map[string]entities.EntityInterface, error) { return nil, err } result["hosts"] = hostsEntity + result["list"] = hostsEntity dsEntity, err := entities.Registry.GetEntityFromValue(serverscom.DedicatedServer{}) if err != nil { diff --git a/cmd/entities/hosts/list.go b/cmd/entities/hosts/list.go index 80b837b..4f1b9fe 100644 --- a/cmd/entities/hosts/list.go +++ b/cmd/entities/hosts/list.go @@ -1,15 +1,13 @@ package hosts import ( - "log" - serverscom "github.com/serverscom/serverscom-go-client/pkg" "github.com/serverscom/srvctl/cmd/base" "github.com/spf13/cobra" ) type hostListOptions struct { - base.BaseLabelsListOptions[serverscom.Host] + base.BaseListOptions[serverscom.Host] rackID string locationID string } @@ -20,11 +18,6 @@ func (o *hostListOptions) AddFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.StringVar(&o.rackID, "rack-id", "", "Filter by rack ID") flags.StringVar(&o.locationID, "location-id", "", "Filter by location ID") - - flags.String("type", "", "") - if err := flags.MarkHidden("type"); err != nil { - log.Fatal(err) - } } func (o *hostListOptions) ApplyToCollection(collection serverscom.Collection[serverscom.Host]) { @@ -50,10 +43,16 @@ func newListCmd(cmdContext *base.CmdContext, hostType *HostTypeCmd) *cobra.Comma return collection } + opts := base.NewListOptions( + &hostListOptions{}, + &base.LabelSelectorOption[serverscom.Host]{}, + &base.SearchPatternOption[serverscom.Host]{}, + ) + entityName := "Hosts" if hostType != nil { entityName = hostType.entityName } - return base.NewListCmd("list", entityName, factory, cmdContext, &hostListOptions{}) + return base.NewListCmd("list", entityName, factory, cmdContext, opts...) } diff --git a/cmd/entities/ssh-keys/list.go b/cmd/entities/ssh-keys/list.go index 91aaafc..857a6fc 100644 --- a/cmd/entities/ssh-keys/list.go +++ b/cmd/entities/ssh-keys/list.go @@ -12,7 +12,10 @@ func newListCmd(cmdContext *base.CmdContext) *cobra.Command { return scClient.SSHKeys.Collection() } - opts := &base.BaseLabelsListOptions[serverscom.SSHKey]{} + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.SSHKey]{}, + &base.LabelSelectorOption[serverscom.SSHKey]{}, + ) - return base.NewListCmd("list", "SSH Keys", factory, cmdContext, opts) + return base.NewListCmd("list", "SSH Keys", factory, cmdContext, opts...) } diff --git a/cmd/entities/ssl/add.go b/cmd/entities/ssl/add.go new file mode 100644 index 0000000..3c0ef17 --- /dev/null +++ b/cmd/entities/ssl/add.go @@ -0,0 +1,73 @@ +package ssl + +import ( + "context" + "fmt" + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type SSLCreator interface { + Create(ctx context.Context, client *serverscom.Client, input any) (any, error) + NewCreateInput() any +} + +type SSLCustomCreateMgr struct{} + +func (c *SSLCustomCreateMgr) Create(ctx context.Context, client *serverscom.Client, input any) (any, error) { + sslInput, ok := input.(*serverscom.SSLCertificateCreateCustomInput) + if !ok { + return nil, fmt.Errorf("invalid input type for custom SSL") + } + return client.SSLCertificates.CreateCustom(ctx, *sslInput) +} + +func (c *SSLCustomCreateMgr) NewCreateInput() any { + return &serverscom.SSLCertificateCreateCustomInput{} +} + +func newAddCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { + var path string + cmd := &cobra.Command{ + Use: "add --input ", + Short: fmt.Sprintf("Create a %s", sslType.entityName), + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + input := sslType.managers.createMgr.NewCreateInput() + + if err := base.ReadInputJSON(path, cmd.InOrStdin(), input); err != nil { + return err + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + server, err := sslType.managers.createMgr.Create(ctx, scClient, input) + if err != nil { + return err + } + + if server != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(server) + } + + return nil + }, + } + + cmd.Flags().StringVarP(&path, "input", "i", "", "path to input file or '-' to read from stdin") + if err := cmd.MarkFlagRequired("input"); err != nil { + log.Fatal(err) + } + + return cmd +} diff --git a/cmd/entities/ssl/delete.go b/cmd/entities/ssl/delete.go new file mode 100644 index 0000000..51bd567 --- /dev/null +++ b/cmd/entities/ssl/delete.go @@ -0,0 +1,53 @@ +package ssl + +import ( + "context" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type SSLDeleter interface { + Delete(ctx context.Context, client *serverscom.Client, id string) error +} + +type SSLCustomDeleteMgr struct{} + +func (d *SSLCustomDeleteMgr) Delete(ctx context.Context, client *serverscom.Client, id string) error { + return client.SSLCertificates.DeleteCustom(ctx, id) +} + +type SSLLeDeleteMgr struct{} + +func (d *SSLLeDeleteMgr) Delete(ctx context.Context, client *serverscom.Client, id string) error { + return client.SSLCertificates.DeleteLE(ctx, id) +} + +func newDeleteCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete an SSL certificate", + Long: "Delete an SSL certificate by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + err := sslType.managers.deleteMgr.Delete(ctx, scClient, id) + if err != nil { + return err + } + + return nil + }, + } + return cmd +} diff --git a/cmd/entities/ssl/get.go b/cmd/entities/ssl/get.go new file mode 100644 index 0000000..514792d --- /dev/null +++ b/cmd/entities/ssl/get.go @@ -0,0 +1,57 @@ +package ssl + +import ( + "context" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +type SSLGetter interface { + Get(ctx context.Context, client *serverscom.Client, id string) (any, error) +} + +type SSLCustomGetMgr struct{} + +func (g *SSLCustomGetMgr) Get(ctx context.Context, client *serverscom.Client, id string) (any, error) { + return client.SSLCertificates.GetCustom(ctx, id) +} + +type SSLLeGetMgr struct{} + +func (g *SSLLeGetMgr) Get(ctx context.Context, client *serverscom.Client, id string) (any, error) { + return client.SSLCertificates.GetLE(ctx, id) +} + +func newGetCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { + cmd := &cobra.Command{ + Use: "get ", + Short: "Get an ssl certificate", + Long: "Get an ssl certificate by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + sslCert, err := sslType.managers.getMgr.Get(ctx, scClient, id) + if err != nil { + return err + } + + if sslCert != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sslCert) + } + return nil + }, + } + return cmd +} diff --git a/cmd/entities/ssl/list.go b/cmd/entities/ssl/list.go new file mode 100644 index 0000000..ee5d458 --- /dev/null +++ b/cmd/entities/ssl/list.go @@ -0,0 +1,33 @@ +package ssl + +import ( + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newListCmd(cmdContext *base.CmdContext, sslType *SSLTypeCmd) *cobra.Command { + factory := func(verbose bool, args ...string) serverscom.Collection[serverscom.SSLCertificate] { + scClient := cmdContext.GetClient().SetVerbose(verbose).GetScClient() + collection := scClient.SSLCertificates.Collection() + + if sslType != nil && sslType.typeFlag != "" { + collection = collection.SetParam("type", sslType.typeFlag) + } + + return collection + } + + opts := base.NewListOptions( + &base.BaseListOptions[serverscom.SSLCertificate]{}, + &base.LabelSelectorOption[serverscom.SSLCertificate]{}, + &base.SearchPatternOption[serverscom.SSLCertificate]{}, + ) + + entityName := "SSL certificates" + if sslType != nil { + entityName = sslType.entityName + } + + return base.NewListCmd("list", entityName, factory, cmdContext, opts...) +} diff --git a/cmd/entities/ssl/ssl.go b/cmd/entities/ssl/ssl.go new file mode 100644 index 0000000..5f9b05e --- /dev/null +++ b/cmd/entities/ssl/ssl.go @@ -0,0 +1,136 @@ +package ssl + +import ( + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/serverscom/srvctl/internal/output/entities" + "github.com/spf13/cobra" +) + +type SSLTypeCmd struct { + use string + shortDesc string + entityName string + typeFlag string + managers SSLManagers + extraCmds []func(*base.CmdContext) *cobra.Command +} + +type SSLManagers struct { + getMgr SSLGetter + createMgr SSLCreator + // for update we use simple commands in sake of simplicity + deleteMgr SSLDeleter +} + +func NewCmd(cmdContext *base.CmdContext) *cobra.Command { + entitiesMap, err := getSSLEntities() + if err != nil { + log.Fatal(err) + } + + sslTypeCmds := []SSLTypeCmd{ + { + use: "custom", + shortDesc: "Manage ssl custom certificates", + entityName: "Custom SSL certificates", + typeFlag: "custom", + managers: SSLManagers{ + getMgr: &SSLCustomGetMgr{}, + createMgr: &SSLCustomCreateMgr{}, + deleteMgr: &SSLCustomDeleteMgr{}, + }, + extraCmds: []func(*base.CmdContext) *cobra.Command{ + newUpdateCustomCmd, + }, + }, + { + use: "le", + shortDesc: "Manage ssl le certificates", + entityName: "LE SSL certificates", + typeFlag: "le", + managers: SSLManagers{ + getMgr: &SSLLeGetMgr{}, + deleteMgr: &SSLLeDeleteMgr{}, + }, + extraCmds: []func(*base.CmdContext) *cobra.Command{ + newUpdateLeCmd, + }, + }, + } + + cmd := &cobra.Command{ + Use: "ssl", + Short: "Manage SSL certificates", + Long: "Manage SSL certificates of different types ( custom, let's encrypt )", + PersistentPreRunE: base.CombinePreRunE( + base.CheckFormatterFlags(cmdContext, entitiesMap), + base.CheckEmptyContexts(cmdContext), + ), + Args: base.NoArgs, + Run: base.UsageRun, + } + + // ssl list cmd + cmd.AddCommand(newListCmd(cmdContext, nil)) + + for _, st := range sslTypeCmds { + cmd.AddCommand(newSSLTypeCmd(cmdContext, st)) + } + + base.AddFormatFlags(cmd) + + return cmd +} + +func newSSLTypeCmd(cmdContext *base.CmdContext, sslTypeCmd SSLTypeCmd) *cobra.Command { + sslCmd := &cobra.Command{ + Use: sslTypeCmd.use, + Short: sslTypeCmd.shortDesc, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + sslCmd.AddCommand(newListCmd(cmdContext, &sslTypeCmd)) + sslCmd.AddCommand(newGetCmd(cmdContext, &sslTypeCmd)) + + if sslTypeCmd.managers.createMgr != nil { + sslCmd.AddCommand(newAddCmd(cmdContext, &sslTypeCmd)) + } + if sslTypeCmd.managers.deleteMgr != nil { + sslCmd.AddCommand(newDeleteCmd(cmdContext, &sslTypeCmd)) + } + + for _, cmdFunc := range sslTypeCmd.extraCmds { + sslCmd.AddCommand(cmdFunc(cmdContext)) + } + + return sslCmd +} + +func getSSLEntities() (map[string]entities.EntityInterface, error) { + result := make(map[string]entities.EntityInterface) + sslEntity, err := entities.Registry.GetEntityFromValue(serverscom.SSLCertificate{}) + if err != nil { + return nil, err + } + result["ssl"] = sslEntity + result["list"] = sslEntity + + sslCustomEntity, err := entities.Registry.GetEntityFromValue(serverscom.SSLCertificateCustom{}) + if err != nil { + return nil, err + } + result["custom"] = sslCustomEntity + + sslLeEntity, err := entities.Registry.GetEntityFromValue(serverscom.SSLCertificateLE{}) + if err != nil { + return nil, err + } + result["le"] = sslLeEntity + + return result, nil +} diff --git a/cmd/entities/ssl/ssl_test.go b/cmd/entities/ssl/ssl_test.go new file mode 100644 index 0000000..cb5c5be --- /dev/null +++ b/cmd/entities/ssl/ssl_test.go @@ -0,0 +1,729 @@ +package ssl + +import ( + "errors" + "path/filepath" + "testing" + "time" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/testutils" + "github.com/serverscom/srvctl/internal/mocks" + + . "github.com/onsi/gomega" + "go.uber.org/mock/gomock" +) + +var ( + fixtureBasePath = filepath.Join("..", "..", "..", "testdata", "entities", "ssl") + fixedTime = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + expiresTime = fixedTime.AddDate(10, 0, 0) + testSha1Fingerpring = "21e84c9a3878673b377f0adf053290e8fc25cb80" + testIssuer = "servers.com" + testId = "testId" + testSSL = serverscom.SSLCertificate{ + ID: testId, + Name: "test-ssl-custom", + Type: "custom", + Issuer: &testIssuer, + Subject: "servers.com", + DomainNames: []string{"servers.com"}, + Sha1Fingerprint: testSha1Fingerpring, + Labels: map[string]string{"foo": "bar"}, + Expires: &expiresTime, + Created: fixedTime, + Updated: fixedTime, + } + testCustomSSL = serverscom.SSLCertificateCustom{ + ID: testId, + Name: "test-ssl-custom", + Type: "custom", + Issuer: &testIssuer, + Subject: "servers.com", + DomainNames: []string{"servers.com"}, + Sha1Fingerprint: testSha1Fingerpring, + Labels: map[string]string{"foo": "bar"}, + Expires: &expiresTime, + Created: fixedTime, + Updated: fixedTime, + } + testLeSSL = serverscom.SSLCertificateLE{ + ID: testId, + Name: "test-ssl-le", + Type: "letsencrypt", + Issuer: &testIssuer, + Subject: "servers.com", + DomainNames: []string{"servers.com"}, + Labels: map[string]string{"foo": "bar"}, + Expires: &expiresTime, + Created: fixedTime, + Updated: fixedTime, + } +) + +func TestAddCustomSSLCmd(t *testing.T) { + testCases := []struct { + name string + output string + args []string + configureMock func(*mocks.MockSSLCertificatesService) + expectedOutput []byte + expectError bool + }{ + { + name: "create custom ssl cert", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.json")), + args: []string{"--input", filepath.Join(fixtureBasePath, "create_custom.json")}, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + CreateCustom(gomock.Any(), serverscom.SSLCertificateCreateCustomInput{ + Name: "test-ssl-custom", + PublicKey: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----", + Labels: map[string]string{"foo": "bar"}, + }). + Return(&testCustomSSL, nil) + }, + }, + { + name: "with error", + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(sslServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "custom", "add"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestGetCustomSSLCmd(t *testing.T) { + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get custom ssl cert in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.txt")), + }, + { + name: "get custom ssl cert JSON format", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.json")), + }, + { + name: "get custom ssl cert YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_custom.yaml")), + }, + { + name: "get custom ssl cert with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + sslServiceHandler.EXPECT(). + GetCustom(gomock.Any(), testId). + Return(&testCustomSSL, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "custom", "get", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestGetLeSSLCmd(t *testing.T) { + testCases := []struct { + name string + id string + output string + expectedOutput []byte + expectError bool + }{ + { + name: "get le ssl cert in default format", + id: testId, + output: "", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_le.txt")), + }, + { + name: "get le ssl cert JSON format", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_le.json")), + }, + { + name: "get le ssl cert YAML format", + id: testId, + output: "yaml", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "get_le.yaml")), + }, + { + name: "get le ssl cert with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + sslServiceHandler.EXPECT(). + GetLE(gomock.Any(), testId). + Return(&testLeSSL, err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "le", "get", tc.id} + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestListSSLCmd(t *testing.T) { + ssl1 := testSSL + ssl2 := testSSL + ssl2.Name = "test-ssl-le" + ssl2.Type = "letsencrypt" + + testCases := []struct { + name string + output string + args []string + expectedOutput []byte + expectError bool + configureMock func(*mocks.MockCollection[serverscom.SSLCertificate]) + }{ + { + name: "list all ssl certs", + output: "json", + args: []string{"-A"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_all.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + Collect(gomock.Any()). + Return([]serverscom.SSLCertificate{ + ssl1, + ssl2, + }, nil) + }, + }, + { + name: "list custom ssl certs", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_custom.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.SSLCertificate{ + ssl1, + }, nil) + }, + }, + { + name: "list le ssl certs", + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_le.json")), + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.SSLCertificate{ + ssl2, + }, nil) + }, + }, + { + name: "list ssl certs with template", + args: []string{"--template", "{{range .}}Name: {{.Name}}\n{{end}}"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_template.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.SSLCertificate{ + ssl1, + ssl2, + }, nil) + }, + }, + { + name: "list ssl certs with pageView", + args: []string{"--page-view"}, + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "list_pageview.txt")), + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + List(gomock.Any()). + Return([]serverscom.SSLCertificate{ + ssl1, + ssl2, + }, nil) + }, + }, + { + name: "list ssl certs with error", + expectError: true, + configureMock: func(mock *mocks.MockCollection[serverscom.SSLCertificate]) { + mock.EXPECT(). + List(gomock.Any()). + Return(nil, errors.New("some error")) + }, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + collectionHandler := mocks.NewMockCollection[serverscom.SSLCertificate](mockCtrl) + + sslServiceHandler.EXPECT(). + Collection(). + Return(collectionHandler). + AnyTimes() + + collectionHandler.EXPECT(). + SetParam(gomock.Any(), gomock.Any()). + Return(collectionHandler). + AnyTimes() + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(collectionHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "list"} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestUpdateCustomSSLCmd(t *testing.T) { + newSSL := testCustomSSL + newSSL.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockSSLCertificatesService) + expectedOutput []byte + expectError bool + }{ + { + name: "update ssl cert", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_custom.json")), + args: []string{"--label", "new=label"}, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + UpdateCustom(gomock.Any(), testId, serverscom.SSLCertificateUpdateCustomInput{ + Labels: map[string]string{"new": "label"}, + }). + Return(&newSSL, nil) + }, + }, + { + name: "update ssl cert with error", + id: testId, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + UpdateCustom(gomock.Any(), testId, serverscom.SSLCertificateUpdateCustomInput{ + Labels: make(map[string]string), + }). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(sslServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "custom", "update", tc.id} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestUpdateLeSSLCmd(t *testing.T) { + newSSL := testLeSSL + newSSL.Labels = map[string]string{"new": "label"} + + testCases := []struct { + name string + id string + output string + args []string + configureMock func(*mocks.MockSSLCertificatesService) + expectedOutput []byte + expectError bool + }{ + { + name: "update ssl cert", + id: testId, + output: "json", + expectedOutput: testutils.ReadFixture(filepath.Join(fixtureBasePath, "update_le.json")), + args: []string{"--label", "new=label"}, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + UpdateLE(gomock.Any(), testId, serverscom.SSLCertificateUpdateLEInput{ + Labels: map[string]string{"new": "label"}, + }). + Return(&newSSL, nil) + }, + }, + { + name: "update ssl cert with error", + id: testId, + configureMock: func(mock *mocks.MockSSLCertificatesService) { + mock.EXPECT(). + UpdateLE(gomock.Any(), testId, serverscom.SSLCertificateUpdateLEInput{ + Labels: make(map[string]string), + }). + Return(nil, errors.New("some error")) + }, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + if tc.configureMock != nil { + tc.configureMock(sslServiceHandler) + } + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "le", "update", tc.id} + if len(tc.args) > 0 { + args = append(args, tc.args...) + } + if tc.output != "" { + args = append(args, "--output", tc.output) + } + + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err := cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + g.Expect(builder.GetOutput()).To(BeEquivalentTo(string(tc.expectedOutput))) + } + }) + } +} + +func TestDeleteCustomSSLKeysCmd(t *testing.T) { + testCases := []struct { + name string + id string + expectError bool + }{ + { + name: "delete ssl cert", + id: testId, + }, + { + name: "delete ssl cert with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + sslServiceHandler.EXPECT(). + DeleteCustom(gomock.Any(), testId). + Return(err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "custom", "delete", tc.id} + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + } + }) + } +} + +func TestDeleteLeSSLKeysCmd(t *testing.T) { + testCases := []struct { + name string + id string + expectError bool + }{ + { + name: "delete ssl cert", + id: testId, + }, + { + name: "delete ssl cert with error", + id: testId, + expectError: true, + }, + } + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + sslServiceHandler := mocks.NewMockSSLCertificatesService(mockCtrl) + + scClient := serverscom.NewClientWithEndpoint("", "") + scClient.SSLCertificates = sslServiceHandler + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + + var err error + if tc.expectError { + err = errors.New("some error") + } + sslServiceHandler.EXPECT(). + DeleteLE(gomock.Any(), testId). + Return(err) + + testCmdContext := testutils.NewTestCmdContext(scClient) + sslCmd := NewCmd(testCmdContext) + + args := []string{"ssl", "le", "delete", tc.id} + builder := testutils.NewTestCommandBuilder(). + WithCommand(sslCmd). + WithArgs(args) + + cmd := builder.Build() + + err = cmd.Execute() + + if tc.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).To(BeNil()) + } + }) + } +} diff --git a/cmd/entities/ssl/update.go b/cmd/entities/ssl/update.go new file mode 100644 index 0000000..5c7127c --- /dev/null +++ b/cmd/entities/ssl/update.go @@ -0,0 +1,99 @@ +package ssl + +import ( + "log" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + "github.com/serverscom/srvctl/cmd/base" + "github.com/spf13/cobra" +) + +func newUpdateCustomCmd(cmdContext *base.CmdContext) *cobra.Command { + var labels []string + + cmd := &cobra.Command{ + Use: "update ", + Short: "Update an ssl custom certificate", + Long: "Update an ssl custom certificate by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + labelsMap, err := base.ParseLabels(labels) + if err != nil { + log.Fatal(err) + } + input := serverscom.SSLCertificateUpdateCustomInput{ + Labels: labelsMap, + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + sshKey, err := scClient.SSLCertificates.UpdateCustom(ctx, id, input) + if err != nil { + return err + } + + if sshKey != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sshKey) + } + return nil + }, + } + + cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") + + return cmd +} + +func newUpdateLeCmd(cmdContext *base.CmdContext) *cobra.Command { + var labels []string + + cmd := &cobra.Command{ + Use: "update ", + Short: "Update an ssl le certificate", + Long: "Update an ssl le certificate by id", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + manager := cmdContext.GetManager() + + ctx, cancel := base.SetupContext(cmd, manager) + defer cancel() + + base.SetupProxy(cmd, manager) + + labelsMap, err := base.ParseLabels(labels) + if err != nil { + log.Fatal(err) + } + input := serverscom.SSLCertificateUpdateLEInput{ + Labels: labelsMap, + } + + scClient := cmdContext.GetClient().SetVerbose(manager.GetVerbose(cmd)).GetScClient() + + id := args[0] + sshKey, err := scClient.SSLCertificates.UpdateLE(ctx, id, input) + if err != nil { + return err + } + + if sshKey != nil { + formatter := cmdContext.GetOrCreateFormatter(cmd) + return formatter.Format(sshKey) + } + return nil + }, + } + + cmd.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "string in key=value format") + + return cmd +} diff --git a/cmd/root.go b/cmd/root.go index b70d390..ee3c036 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,6 +6,7 @@ import ( "github.com/serverscom/srvctl/cmd/context" "github.com/serverscom/srvctl/cmd/entities/hosts" sshkeys "github.com/serverscom/srvctl/cmd/entities/ssh-keys" + "github.com/serverscom/srvctl/cmd/entities/ssl" "github.com/serverscom/srvctl/cmd/login" "github.com/serverscom/srvctl/internal/client" "github.com/spf13/cobra" @@ -38,6 +39,7 @@ func NewRootCmd(version string) *cobra.Command { // resources comands cmd.AddCommand(sshkeys.NewCmd(cmdContext)) cmd.AddCommand(hosts.NewCmd(cmdContext)) + cmd.AddCommand(ssl.NewCmd(cmdContext)) return cmd } diff --git a/go.mod b/go.mod index b567fed..4214043 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,14 @@ require ( github.com/creack/pty v1.1.24 github.com/jmespath/go-jmespath v0.4.0 github.com/onsi/gomega v1.36.2 - github.com/serverscom/serverscom-go-client v1.0.10 + github.com/serverscom/serverscom-go-client v1.0.13 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stoewer/go-strcase v1.3.0 go.uber.org/mock v0.5.0 - golang.org/x/term v0.29.0 - golang.org/x/text v0.22.0 + golang.org/x/term v0.30.0 + golang.org/x/text v0.23.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -35,7 +35,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index b26c192..896c740 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/serverscom/serverscom-go-client v1.0.10 h1:fHND85wR1L4n16SEnmHif/fvcy4xHfpSLIog6kRvZlg= -github.com/serverscom/serverscom-go-client v1.0.10/go.mod h1:BLuNrLl7gNG3y9tEBXkRjPlAI/URY4yIadyvpt0rE2Q= +github.com/serverscom/serverscom-go-client v1.0.13 h1:HjcODZl8M8SEwhbeBGX2D/M3ZZM7I0JGQq717rW792Y= +github.com/serverscom/serverscom-go-client v1.0.13/go.mod h1:o4lNYX+shv5TZ6miuGAaMDJP8y7Z7TdPEhMsCcL9PrU= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -88,14 +88,14 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= diff --git a/internal/mocks/ssl_service.go b/internal/mocks/ssl_service.go new file mode 100644 index 0000000..c967dce --- /dev/null +++ b/internal/mocks/ssl_service.go @@ -0,0 +1,159 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssl_certificates.go +// +// Generated by this command: +// +// mockgen --destination ./internal/mocks/ssl_service.go --package=mocks --source ./vendor/github.com/serverscom/serverscom-go-client/pkg/ssl_certificates.go +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" + gomock "go.uber.org/mock/gomock" +) + +// MockSSLCertificatesService is a mock of SSLCertificatesService interface. +type MockSSLCertificatesService struct { + ctrl *gomock.Controller + recorder *MockSSLCertificatesServiceMockRecorder + isgomock struct{} +} + +// MockSSLCertificatesServiceMockRecorder is the mock recorder for MockSSLCertificatesService. +type MockSSLCertificatesServiceMockRecorder struct { + mock *MockSSLCertificatesService +} + +// NewMockSSLCertificatesService creates a new mock instance. +func NewMockSSLCertificatesService(ctrl *gomock.Controller) *MockSSLCertificatesService { + mock := &MockSSLCertificatesService{ctrl: ctrl} + mock.recorder = &MockSSLCertificatesServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSSLCertificatesService) EXPECT() *MockSSLCertificatesServiceMockRecorder { + return m.recorder +} + +// Collection mocks base method. +func (m *MockSSLCertificatesService) Collection() serverscom.Collection[serverscom.SSLCertificate] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection") + ret0, _ := ret[0].(serverscom.Collection[serverscom.SSLCertificate]) + return ret0 +} + +// Collection indicates an expected call of Collection. +func (mr *MockSSLCertificatesServiceMockRecorder) Collection() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockSSLCertificatesService)(nil).Collection)) +} + +// CreateCustom mocks base method. +func (m *MockSSLCertificatesService) CreateCustom(ctx context.Context, input serverscom.SSLCertificateCreateCustomInput) (*serverscom.SSLCertificateCustom, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCustom", ctx, input) + ret0, _ := ret[0].(*serverscom.SSLCertificateCustom) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCustom indicates an expected call of CreateCustom. +func (mr *MockSSLCertificatesServiceMockRecorder) CreateCustom(ctx, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCustom", reflect.TypeOf((*MockSSLCertificatesService)(nil).CreateCustom), ctx, input) +} + +// DeleteCustom mocks base method. +func (m *MockSSLCertificatesService) DeleteCustom(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCustom", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCustom indicates an expected call of DeleteCustom. +func (mr *MockSSLCertificatesServiceMockRecorder) DeleteCustom(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCustom", reflect.TypeOf((*MockSSLCertificatesService)(nil).DeleteCustom), ctx, id) +} + +// DeleteLE mocks base method. +func (m *MockSSLCertificatesService) DeleteLE(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteLE", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteLE indicates an expected call of DeleteLE. +func (mr *MockSSLCertificatesServiceMockRecorder) DeleteLE(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLE", reflect.TypeOf((*MockSSLCertificatesService)(nil).DeleteLE), ctx, id) +} + +// GetCustom mocks base method. +func (m *MockSSLCertificatesService) GetCustom(ctx context.Context, id string) (*serverscom.SSLCertificateCustom, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCustom", ctx, id) + ret0, _ := ret[0].(*serverscom.SSLCertificateCustom) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCustom indicates an expected call of GetCustom. +func (mr *MockSSLCertificatesServiceMockRecorder) GetCustom(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustom", reflect.TypeOf((*MockSSLCertificatesService)(nil).GetCustom), ctx, id) +} + +// GetLE mocks base method. +func (m *MockSSLCertificatesService) GetLE(ctx context.Context, id string) (*serverscom.SSLCertificateLE, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLE", ctx, id) + ret0, _ := ret[0].(*serverscom.SSLCertificateLE) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLE indicates an expected call of GetLE. +func (mr *MockSSLCertificatesServiceMockRecorder) GetLE(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLE", reflect.TypeOf((*MockSSLCertificatesService)(nil).GetLE), ctx, id) +} + +// UpdateCustom mocks base method. +func (m *MockSSLCertificatesService) UpdateCustom(ctx context.Context, id string, input serverscom.SSLCertificateUpdateCustomInput) (*serverscom.SSLCertificateCustom, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateCustom", ctx, id, input) + ret0, _ := ret[0].(*serverscom.SSLCertificateCustom) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateCustom indicates an expected call of UpdateCustom. +func (mr *MockSSLCertificatesServiceMockRecorder) UpdateCustom(ctx, id, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCustom", reflect.TypeOf((*MockSSLCertificatesService)(nil).UpdateCustom), ctx, id, input) +} + +// UpdateLE mocks base method. +func (m *MockSSLCertificatesService) UpdateLE(ctx context.Context, id string, input serverscom.SSLCertificateUpdateLEInput) (*serverscom.SSLCertificateLE, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateLE", ctx, id, input) + ret0, _ := ret[0].(*serverscom.SSLCertificateLE) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateLE indicates an expected call of UpdateLE. +func (mr *MockSSLCertificatesServiceMockRecorder) UpdateLE(ctx, id, input any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLE", reflect.TypeOf((*MockSSLCertificatesService)(nil).UpdateLE), ctx, id, input) +} diff --git a/internal/output/entities/handlers.go b/internal/output/entities/handlers.go index 789a373..0861ffa 100644 --- a/internal/output/entities/handlers.go +++ b/internal/output/entities/handlers.go @@ -62,7 +62,7 @@ func timeHandler(w io.Writer, v any, indent string, _ *Field) error { } } -func mapHandler(w io.Writer, v interface{}, indent string, _ *Field) error { +func mapPvHandler(w io.Writer, v any, indent string, _ *Field) error { if v == nil { _, err := fmt.Fprintf(w, "%s", indent) return err @@ -114,3 +114,24 @@ func structPVHandler(w io.Writer, v any, indent string, f *Field) error { return nil } + +func slicePvHandler(w io.Writer, v any, indent string, _ *Field) error { + if v == nil { + _, err := fmt.Fprintf(w, "%s", indent) + return err + } + + slice, ok := v.([]string) + if !ok || len(slice) == 0 { + _, err := fmt.Fprintf(w, "%s", indent) + return err + } + + lines := make([]string, 0, len(slice)) + for _, item := range slice { + lines = append(lines, fmt.Sprintf("%s%s", indent, item)) + } + + _, err := fmt.Fprint(w, strings.Join(lines, "\n")) + return err +} diff --git a/internal/output/entities/hosts.go b/internal/output/entities/hosts.go index 6a98586..d79e7c9 100644 --- a/internal/output/entities/hosts.go +++ b/internal/output/entities/hosts.go @@ -84,7 +84,7 @@ func RegisterDedicatedServerDefinition() { {ID: "PrivateIPv4Address", Name: "PrivateIPv4Address", Path: "PrivateIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "PublicIPv4Address", Name: "PublicIPv4Address", Path: "PublicIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "ScheduledRelease", Name: "ScheduledRelease", Path: "ScheduledRelease", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler}, - {ID: "Labels", Name: "Labels", Path: "Labels", ListHandlerFunc: mapHandler, PageViewHandlerFunc: mapHandler}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, getConfigurationDetailsField(), @@ -112,7 +112,7 @@ func RegisterKubernetesBaremetalNodeDefinition() { {ID: "PrivateIPv4Address", Name: "PrivateIPv4Address", Path: "PrivateIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "PublicIPv4Address", Name: "PublicIPv4Address", Path: "PublicIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "ScheduledRelease", Name: "ScheduledRelease", Path: "ScheduledRelease", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler}, - {ID: "Labels", Name: "Labels", Path: "Labels", ListHandlerFunc: mapHandler, PageViewHandlerFunc: mapHandler}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, getConfigurationDetailsField(), @@ -140,7 +140,7 @@ func RegisterSBMServerDefinition() { {ID: "PrivateIPv4Address", Name: "PrivateIPv4Address", Path: "PrivateIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "PublicIPv4Address", Name: "PublicIPv4Address", Path: "PublicIPv4Address", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, {ID: "ScheduledRelease", Name: "ScheduledRelease", Path: "ScheduledRelease", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler}, - {ID: "Labels", Name: "Labels", Path: "Labels", ListHandlerFunc: mapHandler, PageViewHandlerFunc: mapHandler}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, getConfigurationDetailsField(), diff --git a/internal/output/entities/init.go b/internal/output/entities/init.go index a4dd078..0762eea 100644 --- a/internal/output/entities/init.go +++ b/internal/output/entities/init.go @@ -11,4 +11,7 @@ func init() { RegisterDedicatedServerDefinition() RegisterKubernetesBaremetalNodeDefinition() RegisterSBMServerDefinition() + RegisterSSLCertDefinition() + RegisterSSLCertCustomDefinition() + RegisterSSLCertLeDefinition() } diff --git a/internal/output/entities/ssh_keys.go b/internal/output/entities/ssh_keys.go index 58a50d0..e1d01e9 100644 --- a/internal/output/entities/ssh_keys.go +++ b/internal/output/entities/ssh_keys.go @@ -17,7 +17,7 @@ func RegisterSSHKeyDefinition() { fields: []Field{ {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, {ID: "Fingerprint", Name: "Fingerprint", Path: "Fingerprint", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, - {ID: "Labels", Name: "Labels", Path: "Labels", ListHandlerFunc: mapHandler, PageViewHandlerFunc: mapHandler}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, }, diff --git a/internal/output/entities/ssl.go b/internal/output/entities/ssl.go new file mode 100644 index 0000000..125727e --- /dev/null +++ b/internal/output/entities/ssl.go @@ -0,0 +1,80 @@ +package entities + +import ( + "log" + "reflect" + + serverscom "github.com/serverscom/serverscom-go-client/pkg" +) + +var ( + SSLCertType = reflect.TypeOf(serverscom.SSLCertificate{}) + SSLCertCustomType = reflect.TypeOf(serverscom.SSLCertificateCustom{}) + SSLCertLeType = reflect.TypeOf(serverscom.SSLCertificateLE{}) + SSLCertListDefaultFields = []string{"ID", "Name", "Type", "Fingerprint"} +) + +func RegisterSSLCertDefinition() { + e := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Fingerprint", Name: "Fingerprint", Path: "Sha1Fingerprint", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Expires", Name: "Expires", Path: "Expires", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + cmdDefaultFields: map[string][]string{ + "list": SSLCertListDefaultFields, + }, + eType: SSLCertType, + } + if err := Registry.Register(e); err != nil { + log.Fatal(err) + } +} + +func RegisterSSLCertCustomDefinition() { + e := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Issuer", Name: "Issuer", Path: "Issuer", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "Subject", Name: "Subject", Path: "Subject", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "DomainNames", Name: "DomainNames", Path: "DomainNames", PageViewHandlerFunc: slicePvHandler}, + {ID: "Fingerprint", Name: "Fingerprint", Path: "Sha1Fingerprint", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Expires", Name: "Expires", Path: "Expires", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + eType: SSLCertCustomType, + } + if err := Registry.Register(e); err != nil { + log.Fatal(err) + } +} + +func RegisterSSLCertLeDefinition() { + e := &Entity{ + fields: []Field{ + {ID: "ID", Name: "ID", Path: "ID", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Name", Name: "Name", Path: "Name", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Type", Name: "Type", Path: "Type", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "Issuer", Name: "Issuer", Path: "Issuer", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler}, + {ID: "Subject", Name: "Subject", Path: "Subject", ListHandlerFunc: stringHandler, PageViewHandlerFunc: stringHandler, Default: true}, + {ID: "DomainNames", Name: "DomainNames", Path: "DomainNames", PageViewHandlerFunc: slicePvHandler}, + {ID: "Labels", Name: "Labels", Path: "Labels", PageViewHandlerFunc: mapPvHandler}, + {ID: "Expires", Name: "Expires", Path: "Expires", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Created", Name: "Created", Path: "Created", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + {ID: "Updated", Name: "Updated", Path: "Updated", ListHandlerFunc: timeHandler, PageViewHandlerFunc: timeHandler, Default: true}, + }, + eType: SSLCertLeType, + } + if err := Registry.Register(e); err != nil { + log.Fatal(err) + } +} diff --git a/testdata/entities/ssl/create_custom.json b/testdata/entities/ssl/create_custom.json new file mode 100644 index 0000000..3296721 --- /dev/null +++ b/testdata/entities/ssl/create_custom.json @@ -0,0 +1,8 @@ +{ + "name": "test-ssl-custom", + "public_key": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----", + "labels": { + "foo": "bar" + } +} \ No newline at end of file diff --git a/testdata/entities/ssl/get.json b/testdata/entities/ssl/get.json new file mode 100644 index 0000000..be09683 --- /dev/null +++ b/testdata/entities/ssl/get.json @@ -0,0 +1,17 @@ +{ + "id": "testId", + "name": "test-ssl-custom", + "type": "custom", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/ssl/get_custom.json b/testdata/entities/ssl/get_custom.json new file mode 100644 index 0000000..be09683 --- /dev/null +++ b/testdata/entities/ssl/get_custom.json @@ -0,0 +1,17 @@ +{ + "id": "testId", + "name": "test-ssl-custom", + "type": "custom", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/ssl/get_custom.txt b/testdata/entities/ssl/get_custom.txt new file mode 100644 index 0000000..e89308f --- /dev/null +++ b/testdata/entities/ssl/get_custom.txt @@ -0,0 +1,2 @@ +ID Name Type Subject Fingerprint Expires Created Updated +testId test-ssl-custom custom servers.com 21e84c9a3878673b377f0adf053290e8fc25cb80 2035-01-01T12:00:00Z 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/ssl/get_custom.yaml b/testdata/entities/ssl/get_custom.yaml new file mode 100644 index 0000000..7475f50 --- /dev/null +++ b/testdata/entities/ssl/get_custom.yaml @@ -0,0 +1,13 @@ +id: testId +name: test-ssl-custom +type: custom +issuer: servers.com +subject: servers.com +domainnames: + - servers.com +sha1fingerprint: 21e84c9a3878673b377f0adf053290e8fc25cb80 +labels: + foo: bar +expires: 2035-01-01T12:00:00Z +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/ssl/get_le.json b/testdata/entities/ssl/get_le.json new file mode 100644 index 0000000..32aca80 --- /dev/null +++ b/testdata/entities/ssl/get_le.json @@ -0,0 +1,16 @@ +{ + "id": "testId", + "name": "test-ssl-le", + "type": "letsencrypt", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/ssl/get_le.txt b/testdata/entities/ssl/get_le.txt new file mode 100644 index 0000000..ad836dc --- /dev/null +++ b/testdata/entities/ssl/get_le.txt @@ -0,0 +1,2 @@ +ID Name Type Subject Expires Created Updated +testId test-ssl-le letsencrypt servers.com 2035-01-01T12:00:00Z 2025-01-01T12:00:00Z 2025-01-01T12:00:00Z diff --git a/testdata/entities/ssl/get_le.yaml b/testdata/entities/ssl/get_le.yaml new file mode 100644 index 0000000..1bc3d1e --- /dev/null +++ b/testdata/entities/ssl/get_le.yaml @@ -0,0 +1,12 @@ +id: testId +name: test-ssl-le +type: letsencrypt +issuer: servers.com +subject: servers.com +domainnames: + - servers.com +labels: + foo: bar +expires: 2035-01-01T12:00:00Z +created: 2025-01-01T12:00:00Z +updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/ssl/list_all.json b/testdata/entities/ssl/list_all.json new file mode 100644 index 0000000..2c615ce --- /dev/null +++ b/testdata/entities/ssl/list_all.json @@ -0,0 +1,36 @@ +[ + { + "id": "testId", + "name": "test-ssl-custom", + "type": "custom", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + }, + { + "id": "testId", + "name": "test-ssl-le", + "type": "letsencrypt", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/ssl/list_custom.json b/testdata/entities/ssl/list_custom.json new file mode 100644 index 0000000..9e24b5f --- /dev/null +++ b/testdata/entities/ssl/list_custom.json @@ -0,0 +1,19 @@ +[ + { + "id": "testId", + "name": "test-ssl-custom", + "type": "custom", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/ssl/list_le.json b/testdata/entities/ssl/list_le.json new file mode 100644 index 0000000..2f1256f --- /dev/null +++ b/testdata/entities/ssl/list_le.json @@ -0,0 +1,19 @@ +[ + { + "id": "testId", + "name": "test-ssl-le", + "type": "letsencrypt", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "foo": "bar" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +] \ No newline at end of file diff --git a/testdata/entities/ssl/list_pageview.txt b/testdata/entities/ssl/list_pageview.txt new file mode 100644 index 0000000..a3dac8a --- /dev/null +++ b/testdata/entities/ssl/list_pageview.txt @@ -0,0 +1,17 @@ +ID: testId +Name: test-ssl-custom +Type: custom +Fingerprint: 21e84c9a3878673b377f0adf053290e8fc25cb80 +Labels: foo=bar +Expires: 2035-01-01T12:00:00Z +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z +--- +ID: testId +Name: test-ssl-le +Type: letsencrypt +Fingerprint: 21e84c9a3878673b377f0adf053290e8fc25cb80 +Labels: foo=bar +Expires: 2035-01-01T12:00:00Z +Created: 2025-01-01T12:00:00Z +Updated: 2025-01-01T12:00:00Z diff --git a/testdata/entities/ssl/list_template.txt b/testdata/entities/ssl/list_template.txt new file mode 100644 index 0000000..5c35fb2 --- /dev/null +++ b/testdata/entities/ssl/list_template.txt @@ -0,0 +1,2 @@ +Name: test-ssl-custom +Name: test-ssl-le diff --git a/testdata/entities/ssl/update_custom.json b/testdata/entities/ssl/update_custom.json new file mode 100644 index 0000000..f9a3fa1 --- /dev/null +++ b/testdata/entities/ssl/update_custom.json @@ -0,0 +1,17 @@ +{ + "id": "testId", + "name": "test-ssl-custom", + "type": "custom", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "sha1_fingerprint": "21e84c9a3878673b377f0adf053290e8fc25cb80", + "labels": { + "new": "label" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file diff --git a/testdata/entities/ssl/update_le.json b/testdata/entities/ssl/update_le.json new file mode 100644 index 0000000..0e9b6b6 --- /dev/null +++ b/testdata/entities/ssl/update_le.json @@ -0,0 +1,16 @@ +{ + "id": "testId", + "name": "test-ssl-le", + "type": "letsencrypt", + "issuer": "servers.com", + "subject": "servers.com", + "domain_names": [ + "servers.com" + ], + "labels": { + "new": "label" + }, + "expires_at": "2035-01-01T12:00:00Z", + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" +} \ No newline at end of file