Skip to content

Commit d764ff1

Browse files
committed
comments
1 parent dfbdd2b commit d764ff1

9 files changed

Lines changed: 88 additions & 102 deletions

File tree

commands/cfg/whoami.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,22 @@ func handleProvenance(c *core.CommandConfig, cl *client.Client, authErr error) e
7676
// Object Storage credential provenance
7777
_, _, akSrc, skSrc, _ := client.ResolveObjectStorageCredentials()
7878

79-
b.WriteString("\nObject Storage credential sources:\n")
79+
b.WriteString("\nFor Object Storage, in order of priority:\n")
8080

81-
b.WriteString(" Access Key:\n")
82-
for i, src := range client.ObjectStorageAccessKeyOrder {
83-
if akSrc == src {
84-
b.WriteString(fmt.Sprintf(" * [%d] %s (USED)\n", i+1, src))
85-
} else {
86-
b.WriteString(fmt.Sprintf(" [%d] %s\n", i+1, src))
87-
}
81+
type osPair struct {
82+
label string
83+
akSrc client.ObjectStorageAccessKeySource
84+
skSrc client.ObjectStorageSecretKeySource
8885
}
89-
90-
b.WriteString(" Secret Key:\n")
91-
for i, src := range client.ObjectStorageSecretKeyOrder {
92-
if skSrc == src {
93-
b.WriteString(fmt.Sprintf(" * [%d] %s (USED)\n", i+1, src))
86+
pairs := []osPair{
87+
{"environment variables: IONOS_S3_ACCESS_KEY, IONOS_S3_SECRET_KEY", client.ObjectStorageAccessKeyEnv, client.ObjectStorageSecretKeyEnv},
88+
{"credentials from config file: s3AccessKey, s3SecretKey", client.ObjectStorageAccessKeyCfg, client.ObjectStorageSecretKeyCfg},
89+
}
90+
for i, p := range pairs {
91+
if akSrc == p.akSrc && skSrc == p.skSrc {
92+
b.WriteString(fmt.Sprintf("* [%d] %s (USED)\n", i+1, p.label))
9493
} else {
95-
b.WriteString(fmt.Sprintf(" [%d] %s\n", i+1, src))
94+
b.WriteString(fmt.Sprintf(" [%d] %s\n", i+1, p.label))
9695
}
9796
}
9897

commands/object-storage/bucket/create.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package bucket
22

33
import (
44
"context"
5-
"fmt"
65

76
"github.com/ionos-cloud/sdk-go-bundle/products/objectstorage/v2"
87
"github.com/spf13/viper"
98

109
"github.com/ionos-cloud/ionosctl/v6/internal/client"
1110
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
1211
"github.com/ionos-cloud/ionosctl/v6/internal/core"
12+
"github.com/ionos-cloud/ionosctl/v6/internal/printer/table"
1313
)
1414

1515
func CreateBucketCmd() *core.Command {
@@ -19,7 +19,7 @@ func CreateBucketCmd() *core.Command {
1919
Verb: "create",
2020
Aliases: []string{"c"},
2121
ShortDesc: "Create a contract-owned bucket",
22-
Example: "ionosctl object-storage bucket create --name my-bucket\nionosctl object-storage bucket create --name my-bucket --region eu-central-3\nionosctl object-storage bucket create --name my-bucket --object-lock",
22+
Example: "ionosctl object-storage bucket create --name my-bucket\nionosctl object-storage bucket create --name my-bucket --location eu-central-3\nionosctl object-storage bucket create --name my-bucket --object-lock",
2323
PreCmdRun: func(c *core.PreCommandConfig) error {
2424
return core.CheckRequiredFlags(c.Command, c.NS, constants.FlagName)
2525
},
@@ -38,8 +38,13 @@ func CreateBucketCmd() *core.Command {
3838
return err
3939
}
4040

41-
fmt.Fprintf(c.Command.Command.OutOrStdout(), "Bucket %q created successfully in region %q\n", name, location)
42-
return nil
41+
info, err := getBucketInfo(c.Context, name)
42+
if err != nil {
43+
return err
44+
}
45+
46+
cols, _ := c.Command.Command.Flags().GetStringSlice(constants.ArgCols)
47+
return c.Out(table.Sprint(allCols, info, cols))
4348
},
4449
InitClient: false,
4550
})

commands/object-storage/bucket/get.go

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@ type bucketInfo struct {
2020
Region string `json:"Region"`
2121
}
2222

23+
// getBucketInfo fetches metadata for a single bucket by name.
24+
// S3 has no single-bucket GET endpoint; ListBuckets is the only way to retrieve creation date.
25+
func getBucketInfo(ctx context.Context, name string) (*bucketInfo, error) {
26+
result, _, err := client.MustObjectStorage().ObjectStorageClient.BucketsApi.ListBuckets(ctx).Execute()
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
var found *bucketInfo
32+
for _, b := range result.GetBuckets() {
33+
if b.GetName() == name {
34+
found = &bucketInfo{
35+
Name: b.GetName(),
36+
CreationDate: b.GetCreationDate(),
37+
}
38+
break
39+
}
40+
}
41+
if found == nil {
42+
return nil, fmt.Errorf("bucket %q not found", name)
43+
}
44+
45+
loc, _, err := client.MustObjectStorage().ObjectStorageClient.BucketsApi.GetBucketLocation(ctx, name).Execute()
46+
if err == nil && loc != nil {
47+
found.Region = loc.GetLocationConstraint()
48+
}
49+
50+
return found, nil
51+
}
52+
2353
func GetBucketCmd() *core.Command {
2454
cmd := core.NewCommand(context.Background(), nil, core.CommandBuilder{
2555
Namespace: "object-storage",
@@ -34,32 +64,11 @@ func GetBucketCmd() *core.Command {
3464
CmdRun: func(c *core.CommandConfig) error {
3565
name := viper.GetString(core.GetFlagName(c.NS, constants.FlagName))
3666

37-
// S3 has no API to get a single bucket's metadata (creation date).
38-
// ListBuckets is the only way to retrieve it.
39-
result, _, err := client.MustObjectStorage().ObjectStorageClient.BucketsApi.ListBuckets(c.Context).Execute()
67+
found, err := getBucketInfo(c.Context, name)
4068
if err != nil {
4169
return err
4270
}
4371

44-
var found *bucketInfo
45-
for _, b := range result.GetBuckets() {
46-
if b.GetName() == name {
47-
found = &bucketInfo{
48-
Name: b.GetName(),
49-
CreationDate: b.GetCreationDate(),
50-
}
51-
break
52-
}
53-
}
54-
if found == nil {
55-
return fmt.Errorf("bucket %q not found", name)
56-
}
57-
58-
loc, _, err := client.MustObjectStorage().ObjectStorageClient.BucketsApi.GetBucketLocation(c.Context, name).Execute()
59-
if err == nil && loc != nil {
60-
found.Region = loc.GetLocationConstraint()
61-
}
62-
6372
cols, _ := c.Command.Command.Flags().GetStringSlice(constants.ArgCols)
6473
return c.Out(table.Sprint(allCols, found, cols))
6574
},

commands/object-storage/bucket/head.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package bucket
22

33
import (
44
"context"
5+
"fmt"
56

67
"github.com/spf13/cobra"
78
"github.com/spf13/viper"
@@ -41,7 +42,7 @@ func HeadBucketCmd() *core.Command {
4142

4243
_, err := client.MustObjectStorage().ObjectStorageClient.BucketsApi.HeadBucket(c.Context, name).Execute()
4344
if err != nil {
44-
return err
45+
return fmt.Errorf("checking if bucket %q exists: %w", name, err)
4546
}
4647

4748
result := headResult{

commands/object-storage/bucket/list.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func ListBucketsCmd() *core.Command {
1919
Verb: "list",
2020
Aliases: []string{"ls"},
2121
ShortDesc: "List all contract-owned buckets",
22-
Example: "ionosctl object-storage bucket list\nionosctl object-storage bucket list --region eu-central-3",
22+
Example: "ionosctl object-storage bucket list\nionosctl object-storage bucket list --location eu-central-3",
2323
PreCmdRun: func(c *core.PreCommandConfig) error {
2424
return nil
2525
},
@@ -40,7 +40,11 @@ func ListBucketsCmd() *core.Command {
4040
loc, apiResp, locErr := client.MustObjectStorage().ObjectStorageClient.BucketsApi.GetBucketLocation(c.Context, b.GetName()).Execute()
4141
if locErr != nil {
4242
c.Verbose("failed to get location for bucket %q: %v", b.GetName(), locErr)
43-
bi.Region = fmt.Sprintf("<%s>", apiResp.Status)
43+
if apiResp != nil {
44+
bi.Region = fmt.Sprintf("<%s>", apiResp.Status)
45+
} else {
46+
bi.Region = "<unknown>"
47+
}
4448
} else {
4549
bi.Region = loc.GetLocationConstraint()
4650
}

commands/object-storage/object/list.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"time"
77

8+
humanize "github.com/dustin/go-humanize"
89
objectstorage "github.com/ionos-cloud/sdk-go-bundle/products/objectstorage/v2"
910
"github.com/spf13/cobra"
1011
"github.com/spf13/viper"
@@ -31,7 +32,7 @@ var listCols = []table.Column{
3132

3233
type listObjectInfo struct {
3334
Key string `json:"Key"`
34-
Size int32 `json:"Size"`
35+
Size string `json:"Size"`
3536
LastModified time.Time `json:"LastModified"`
3637
StorageClass string `json:"StorageClass"`
3738
ETag string `json:"ETag"`
@@ -129,7 +130,7 @@ func convertObjects(objects []objectstorage.Object) []listObjectInfo {
129130
for _, obj := range objects {
130131
info := listObjectInfo{
131132
Key: obj.GetKey(),
132-
Size: obj.GetSize(),
133+
Size: humanize.IBytes(uint64(int64(obj.GetSize()))),
133134
ETag: obj.GetETag(),
134135
}
135136
if obj.LastModified != nil {

docs/subcommands/Object-Storage/bucket/create.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Create a contract-owned bucket
6262

6363
```text
6464
ionosctl object-storage bucket create --name my-bucket
65-
ionosctl object-storage bucket create --name my-bucket --region eu-central-3
65+
ionosctl object-storage bucket create --name my-bucket --location eu-central-3
6666
ionosctl object-storage bucket create --name my-bucket --object-lock
6767
```
6868

docs/subcommands/Object-Storage/bucket/list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,6 @@ List all contract-owned buckets
6060

6161
```text
6262
ionosctl object-storage bucket list
63-
ionosctl object-storage bucket list --region eu-central-3
63+
ionosctl object-storage bucket list --location eu-central-3
6464
```
6565

internal/client/objectstorageclient.go

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -67,67 +67,34 @@ var (
6767
)
6868

6969
// ResolveObjectStorageCredentials resolves Object Storage access and secret keys, tracking their source.
70-
// Credentials are resolved in priority order:
71-
// 1. Environment variables IONOS_S3_ACCESS_KEY / IONOS_S3_SECRET_KEY
72-
// 2. s3AccessKey / s3SecretKey in the current ionosctl config profile
70+
// Both keys must come from the same source; mixing is not allowed. Priority order:
71+
// 1. Environment variables IONOS_S3_ACCESS_KEY / IONOS_S3_SECRET_KEY (both must be set)
72+
// 2. s3AccessKey / s3SecretKey in the current ionosctl config profile (both must be set)
7373
func ResolveObjectStorageCredentials() (accessKey, secretKey string, akSrc ObjectStorageAccessKeySource, skSrc ObjectStorageSecretKeySource, err error) {
74-
src, cfgErr := retrieveConfigFile()
75-
if cfgErr != nil {
76-
return accessKey, secretKey, akSrc, skSrc, fmt.Errorf("failed to retrieve config file: %w", cfgErr)
74+
// Priority 1: both from environment variables
75+
envAccessKey := os.Getenv(shared.IonosS3AccessKeyEnvVar)
76+
envSecretKey := os.Getenv(shared.IonosS3SecretKeyEnvVar)
77+
if envAccessKey != "" && envSecretKey != "" {
78+
return envAccessKey, envSecretKey, ObjectStorageAccessKeyEnv, ObjectStorageSecretKeyEnv, nil
7779
}
7880

79-
accessKey = os.Getenv(shared.IonosS3AccessKeyEnvVar)
80-
if accessKey != "" {
81-
akSrc = ObjectStorageAccessKeyEnv
82-
}
83-
84-
secretKey = os.Getenv(shared.IonosS3SecretKeyEnvVar)
85-
if secretKey != "" {
86-
skSrc = ObjectStorageSecretKeyEnv
87-
}
88-
89-
// Fall back to config file if either key is missing
90-
if accessKey == "" || secretKey == "" {
91-
accessKey, akSrc, secretKey, skSrc = fillObjectStorageCredsFromConfig(src, accessKey, akSrc, secretKey, skSrc)
92-
}
93-
94-
if accessKey == "" {
95-
akSrc = ObjectStorageAccessKeyNone
96-
}
97-
if secretKey == "" {
98-
skSrc = ObjectStorageSecretKeyNone
81+
// Priority 2: both from config file
82+
src, cfgErr := retrieveConfigFile()
83+
if cfgErr != nil {
84+
return "", "", ObjectStorageAccessKeyNone, ObjectStorageSecretKeyNone,
85+
fmt.Errorf("failed to retrieve config file: %w", cfgErr)
9986
}
100-
101-
if accessKey == "" || secretKey == "" {
102-
return "", "", akSrc, skSrc, fmt.Errorf(
103-
"object storage credentials not found. Set %s and %s environment variables, or configure s3AccessKey/s3SecretKey in your ionosctl profile",
104-
shared.IonosS3AccessKeyEnvVar, shared.IonosS3SecretKeyEnvVar,
105-
)
87+
if src.Config != nil && src.Config.GetCurrentProfile() != nil {
88+
creds := src.Config.GetCurrentProfile().Credentials
89+
if creds.S3AccessKey != "" && creds.S3SecretKey != "" {
90+
return creds.S3AccessKey, creds.S3SecretKey, ObjectStorageAccessKeyCfg, ObjectStorageSecretKeyCfg, nil
91+
}
10692
}
10793

108-
return accessKey, secretKey, akSrc, skSrc, nil
109-
}
110-
111-
// fillObjectStorageCredsFromConfig fills in blank Object Storage access/secret keys from the config
112-
// file's current profile, returning the (possibly updated) values and their sources.
113-
func fillObjectStorageCredsFromConfig(
114-
src ConfigSource,
115-
accessKey string, akSrc ObjectStorageAccessKeySource,
116-
secretKey string, skSrc ObjectStorageSecretKeySource,
117-
) (string, ObjectStorageAccessKeySource, string, ObjectStorageSecretKeySource) {
118-
if src.Config == nil || src.Config.GetCurrentProfile() == nil {
119-
return accessKey, akSrc, secretKey, skSrc
120-
}
121-
creds := src.Config.GetCurrentProfile().Credentials
122-
if accessKey == "" && creds.S3AccessKey != "" {
123-
accessKey = creds.S3AccessKey
124-
akSrc = ObjectStorageAccessKeyCfg
125-
}
126-
if secretKey == "" && creds.S3SecretKey != "" {
127-
secretKey = creds.S3SecretKey
128-
skSrc = ObjectStorageSecretKeyCfg
129-
}
130-
return accessKey, akSrc, secretKey, skSrc
94+
return "", "", ObjectStorageAccessKeyNone, ObjectStorageSecretKeyNone, fmt.Errorf(
95+
"object storage credentials not found. Set %s and %s environment variables, or configure s3AccessKey/s3SecretKey in your ionosctl profile",
96+
shared.IonosS3AccessKeyEnvVar, shared.IonosS3SecretKeyEnvVar,
97+
)
13198
}
13299

133100
// newObjectStorageClient builds a new ObjectStorageClient for the given endpoint.

0 commit comments

Comments
 (0)