Skip to content

Commit

Permalink
fix: integer fields are displayed as floats when using create with o=…
Browse files Browse the repository at this point in the history
…yaml (#651)

Continuation #650, closes #638

When outputting a resource after creation, it is first converted into a
schema struct. This fixes bugs like #638 and also makes all schema
outputs (describe, list, create) consistent.

---------

Co-authored-by: jo <[email protected]>
  • Loading branch information
phm07 and jooola authored Dec 20, 2023
1 parent 5a2dbb2 commit 2d42fcd
Show file tree
Hide file tree
Showing 33 changed files with 263 additions and 456 deletions.
24 changes: 6 additions & 18 deletions internal/cmd/base/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package base

import (
"context"
"encoding/json"
"io"
"os"

"github.com/spf13/cobra"
Expand All @@ -12,14 +10,15 @@ import (
"github.com/hetznercloud/cli/internal/cmd/util"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

// CreateCmd allows defining commands for resource creation
type CreateCmd struct {
BaseCobraCommand func(hcapi2.Client) *cobra.Command
Run func(context.Context, hcapi2.Client, state.ActionWaiter, *cobra.Command, []string) (*hcloud.Response, any, error)
PrintResource func(context.Context, hcapi2.Client, *cobra.Command, any)
// Run is the function that will be called when the command is executed.
// It should return the created resource, the schema of the resource and an error.
Run func(context.Context, hcapi2.Client, state.ActionWaiter, *cobra.Command, []string) (any, any, error)
PrintResource func(context.Context, hcapi2.Client, *cobra.Command, any)
}

// CobraCommand creates a command that can be registered with cobra.
Expand Down Expand Up @@ -53,29 +52,18 @@ func (cc *CreateCmd) CobraCommand(
cmd.SetOut(os.Stdout)
}

response, resource, err := cc.Run(ctx, client, actionWaiter, cmd, args)
resource, schema, err := cc.Run(ctx, client, actionWaiter, cmd, args)
if err != nil {
return err
}

if isSchema {
bytes, _ := io.ReadAll(response.Body)

var schema map[string]any
if err := json.Unmarshal(bytes, &schema); err != nil {
return err
}

delete(schema, "action")
delete(schema, "actions")
delete(schema, "next_actions")

if outputFlags.IsSet("json") {
return util.DescribeJSON(schema)
} else {
return util.DescribeYAML(schema)
}
} else if resource != nil {
} else if cc.PrintResource != nil && resource != nil {
cc.PrintResource(ctx, client, cmd, resource)
}
return nil
Expand Down
28 changes: 14 additions & 14 deletions internal/cmd/certificate/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,26 @@ var CreateCmd = base.CreateCmd{

return cmd
},
Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, strings []string) (*hcloud.Response, any, error) {
Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, strings []string) (any, any, error) {
certType, err := cmd.Flags().GetString("type")
if err != nil {
return nil, nil, err
}
var cert *hcloud.Certificate
switch hcloud.CertificateType(certType) {
case hcloud.CertificateTypeManaged:
response, err := createManaged(ctx, client, waiter, cmd)
return response, nil, err
cert, err = createManaged(ctx, client, waiter, cmd)
default: // Uploaded
response, err := createUploaded(ctx, client, cmd)
return response, nil, err
cert, err = createUploaded(ctx, client, cmd)
}
},
PrintResource: func(_ context.Context, _ hcapi2.Client, _ *cobra.Command, _ any) {
// no-op
if err != nil {
return nil, nil, err
}
return cert, util.Wrap("certificate", hcloud.SchemaFromCertificate(cert)), nil
},
}

func createUploaded(ctx context.Context, client hcapi2.Client, cmd *cobra.Command) (*hcloud.Response, error) {
func createUploaded(ctx context.Context, client hcapi2.Client, cmd *cobra.Command) (*hcloud.Certificate, error) {
var (
name string
certFile, keyFile string
Expand Down Expand Up @@ -96,15 +96,15 @@ func createUploaded(ctx context.Context, client hcapi2.Client, cmd *cobra.Comman
Certificate: string(certPEM),
PrivateKey: string(keyPEM),
}
cert, response, err := client.Certificate().Create(ctx, createOpts)
cert, _, err = client.Certificate().Create(ctx, createOpts)
if err != nil {
return nil, err
}
cmd.Printf("Certificate %d created\n", cert.ID)
return response, nil
return cert, nil
}

func createManaged(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command) (*hcloud.Response, error) {
func createManaged(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command) (*hcloud.Certificate, error) {
var (
name string
domains []string
Expand All @@ -127,13 +127,13 @@ func createManaged(ctx context.Context, client hcapi2.Client, waiter state.Actio
Type: hcloud.CertificateTypeManaged,
DomainNames: domains,
}
res, response, err := client.Certificate().CreateCertificate(ctx, createOpts)
res, _, err = client.Certificate().CreateCertificate(ctx, createOpts)
if err != nil {
return nil, err
}
if err := waiter.ActionProgress(ctx, res.Action); err != nil {
return nil, err
}
cmd.Printf("Certificate %d created\n", res.Certificate.ID)
return response, nil
return res.Certificate, nil
}
88 changes: 31 additions & 57 deletions internal/cmd/certificate/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/hetznercloud/cli/internal/testutil"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

//go:embed testdata/managed_create_response.json
Expand Down Expand Up @@ -68,33 +67,6 @@ func TestCreateManagedJSON(t *testing.T) {
fx.ActionWaiter)
fx.ExpectEnsureToken()

response, err := testutil.MockResponse(&schema.CertificateCreateResponse{
Certificate: schema.Certificate{
ID: 123,
Name: "test",
Type: string(hcloud.CertificateTypeManaged),
Created: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidBefore: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidAfter: time.Date(2036, 8, 12, 12, 0, 0, 0, time.UTC),
DomainNames: []string{"example.com"},
Labels: map[string]string{"key": "value"},
UsedBy: []schema.CertificateUsedByRef{{
ID: 123,
Type: string(hcloud.CertificateUsedByRefTypeLoadBalancer),
}},
Status: &schema.CertificateStatusRef{
Error: &schema.Error{
Code: "cert_error",
Message: "Certificate error",
},
},
},
})

if err != nil {
t.Fatal(err)
}

fx.Client.CertificateClient.EXPECT().
CreateCertificate(gomock.Any(), hcloud.CertificateCreateOpts{
Name: "test",
Expand All @@ -103,13 +75,27 @@ func TestCreateManagedJSON(t *testing.T) {
}).
Return(hcloud.CertificateCreateResult{
Certificate: &hcloud.Certificate{
ID: 123,
Name: "test",
Type: hcloud.CertificateTypeManaged,
DomainNames: []string{"example.com"},
ID: 123,
Name: "test",
Type: hcloud.CertificateTypeManaged,
Created: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidBefore: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidAfter: time.Date(2036, 8, 12, 12, 0, 0, 0, time.UTC),
DomainNames: []string{"example.com"},
Labels: map[string]string{"key": "value"},
UsedBy: []hcloud.CertificateUsedByRef{{
ID: 123,
Type: hcloud.CertificateUsedByRefTypeLoadBalancer,
}},
Status: &hcloud.CertificateStatus{
Error: &hcloud.Error{
Code: "cert_error",
Message: "Certificate error",
},
},
},
Action: &hcloud.Action{ID: 321},
}, response, nil)
}, nil, nil)
fx.ActionWaiter.EXPECT().
ActionProgress(gomock.Any(), &hcloud.Action{ID: 321})

Expand Down Expand Up @@ -166,39 +152,27 @@ func TestCreateUploadedJSON(t *testing.T) {
fx.ActionWaiter)
fx.ExpectEnsureToken()

response, err := testutil.MockResponse(&schema.CertificateCreateResponse{
Certificate: schema.Certificate{
fx.Client.CertificateClient.EXPECT().
Create(gomock.Any(), hcloud.CertificateCreateOpts{
Name: "test",
Type: hcloud.CertificateTypeUploaded,
Certificate: "certificate file content",
PrivateKey: "key file content",
}).
Return(&hcloud.Certificate{
ID: 123,
Name: "test",
Type: string(hcloud.CertificateTypeUploaded),
Type: hcloud.CertificateTypeUploaded,
Created: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidBefore: time.Date(2020, 8, 24, 12, 0, 0, 0, time.UTC),
NotValidAfter: time.Date(2036, 8, 12, 12, 0, 0, 0, time.UTC),
Labels: map[string]string{"key": "value"},
Fingerprint: "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
UsedBy: []schema.CertificateUsedByRef{{
UsedBy: []hcloud.CertificateUsedByRef{{
ID: 123,
Type: string(hcloud.CertificateUsedByRefTypeLoadBalancer),
Type: hcloud.CertificateUsedByRefTypeLoadBalancer,
}},
},
})

if err != nil {
t.Fatal(err)
}

fx.Client.CertificateClient.EXPECT().
Create(gomock.Any(), hcloud.CertificateCreateOpts{
Name: "test",
Type: hcloud.CertificateTypeUploaded,
Certificate: "certificate file content",
PrivateKey: "key file content",
}).
Return(&hcloud.Certificate{
ID: 123,
Name: "test",
Type: hcloud.CertificateTypeUploaded,
}, response, nil)
}, nil, nil)

jsonOut, out, err := fx.Run(cmd, []string{"-o=json", "--name", "test", "--key-file", "testdata/key.pem", "--cert-file", "testdata/cert.pem"})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@
}
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
}
]
}
}
}
7 changes: 4 additions & 3 deletions internal/cmd/firewall/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/spf13/cobra"

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/cmd/util"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
Expand All @@ -32,7 +33,7 @@ var CreateCmd = base.CreateCmd{
cmd.Flags().String("rules-file", "", "JSON file containing your routes (use - to read from stdin). The structure of the file needs to be the same as within the API: https://docs.hetzner.cloud/#firewalls-get-a-firewall ")
return cmd
},
Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, strings []string) (*hcloud.Response, any, error) {
Run: func(ctx context.Context, client hcapi2.Client, waiter state.ActionWaiter, cmd *cobra.Command, strings []string) (any, any, error) {
name, _ := cmd.Flags().GetString("name")
labels, _ := cmd.Flags().GetStringToString("label")

Expand Down Expand Up @@ -78,7 +79,7 @@ var CreateCmd = base.CreateCmd{
}
}

result, response, err := client.Firewall().Create(ctx, opts)
result, _, err := client.Firewall().Create(ctx, opts)
if err != nil {
return nil, nil, err
}
Expand All @@ -89,6 +90,6 @@ var CreateCmd = base.CreateCmd{

cmd.Printf("Firewall %d created\n", result.Firewall.ID)

return response, nil, err
return result.Firewall, util.Wrap("firewall", hcloud.SchemaFromFirewall(result.Firewall)), err
},
}
50 changes: 19 additions & 31 deletions internal/cmd/firewall/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package firewall
import (
"context"
_ "embed"
"net"
"testing"
"time"

Expand All @@ -11,7 +12,6 @@ import (

"github.com/hetznercloud/cli/internal/testutil"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/schema"
)

//go:embed testdata/create_response.json
Expand Down Expand Up @@ -62,45 +62,33 @@ func TestCreateJSON(t *testing.T) {
fx.ActionWaiter)
fx.ExpectEnsureToken()

response, err := testutil.MockResponse(&schema.FirewallCreateResponse{
Firewall: schema.Firewall{
ID: 123,
Name: "test",
Created: time.Date(2016, 1, 30, 23, 50, 0, 0, time.UTC),
AppliedTo: []schema.FirewallResource{
{Type: "server", Server: &schema.FirewallResourceServer{
ID: 1,
}},
},
Labels: make(map[string]string),
Rules: []schema.FirewallRule{
{
Direction: "in",
SourceIPs: make([]string, 0),
Protocol: "tcp",
Port: hcloud.Ptr("22"),
},
},
},
Actions: make([]schema.Action, 0),
})

if err != nil {
t.Fatal(err)
}

fx.Client.FirewallClient.EXPECT().
Create(gomock.Any(), hcloud.FirewallCreateOpts{
Name: "test",
Labels: make(map[string]string),
}).
Return(hcloud.FirewallCreateResult{
Firewall: &hcloud.Firewall{
ID: 123,
Name: "test",
ID: 123,
Name: "test",
Created: time.Date(2016, 1, 30, 23, 50, 0, 0, time.UTC),
AppliedTo: []hcloud.FirewallResource{
{Type: "server", Server: &hcloud.FirewallResourceServer{
ID: 1,
}},
},
Labels: make(map[string]string),
Rules: []hcloud.FirewallRule{
{
Direction: "in",
SourceIPs: []net.IPNet{},
Protocol: "tcp",
Port: hcloud.Ptr("22"),
},
},
},
Actions: []*hcloud.Action{{ID: 321}},
}, response, nil)
}, nil, nil)
fx.ActionWaiter.EXPECT().
WaitForActions(gomock.Any(), []*hcloud.Action{{ID: 321}})

Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/firewall/testdata/create_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
}
]
}
}
}
Loading

0 comments on commit 2d42fcd

Please sign in to comment.