~bigbes/ci-cacher

ref: v0.1.0 ci-cacher/cmd/common.go -rw-r--r-- 3.6 KiB
176b6cb9 — Eugene Blikh Bump VERSION to 0.1.0 for tag 2 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package cmd

import (
	"fmt"
	"runtime"

	"github.com/spf13/cobra"

	"go.bigb.es/cacher/internal/config"
	"go.bigb.es/cacher/internal/hash"
	"go.bigb.es/cacher/internal/s3"
)

// Flags overridable per command (flag > env > config > defaults).
var (
	flagEndpoint   string
	flagRegion     string
	flagBucket     string
	flagPrefix     string
	flagArchSuffix string // "true"/"false"/"" (unset)
	flagKeyFile    string
	flagSecretFile string

	// Key derivation (attached by addKeyFlags).
	flagHashFrom   []string
	flagHashLength int
)

// addKeyFlags attaches --hash-from / --hash-length. Use on every command
// that takes a <key> argument.
func addKeyFlags(c *cobra.Command) {
	c.Flags().StringSliceVar(&flagHashFrom, "hash-from", nil,
		"path(s) whose sha256 is substituted for {hash} in the key (repeatable; file or directory)")
	c.Flags().IntVar(&flagHashLength, "hash-length", hash.DefaultLength,
		"truncate the derived hash to N hex chars")
}

// resolveKey applies --hash-from and --arch-suffix to the user-supplied
// key template. With no --hash-from, the template is returned verbatim
// (plus arch suffix if enabled).
func resolveKey(keyTemplate string, cfg config.Config) (string, error) {
	var derived string
	if len(flagHashFrom) > 0 {
		var err error
		derived, err = hash.Derive(flagHashFrom, flagHashLength)
		if err != nil {
			return "", err
		}
	}
	return hash.ApplyTemplate(keyTemplate, derived, runtime.GOOS, runtime.GOARCH, cfg.ArchSuffix), nil
}

// addS3Flags attaches the standard S3 override flags to a command. Most
// commands accept them so single-shot invocations can override config.
func addS3Flags(c *cobra.Command) {
	c.Flags().StringVar(&flagEndpoint, "endpoint", "", "S3 endpoint URL (overrides config)")
	c.Flags().StringVar(&flagRegion, "region", "", "S3 region (overrides config)")
	c.Flags().StringVar(&flagBucket, "bucket", "", "S3 bucket (overrides config)")
	c.Flags().StringVar(&flagPrefix, "prefix", "", "S3 key prefix (overrides config)")
	c.Flags().StringVar(&flagArchSuffix, "arch-suffix", "", "append -<goos>-<goarch> to key (true|false; default from config)")
	c.Flags().StringVar(&flagKeyFile, "key-file", "", "path to S3 access key id file (overrides config)")
	c.Flags().StringVar(&flagSecretFile, "secret-file", "", "path to S3 secret file (overrides config)")
}

// loadConfig merges file → env → flag overrides.
func loadConfig() (config.Config, error) {
	cfg, err := config.Load(flagConfigPath)
	if err != nil {
		return cfg, err
	}
	if err := config.ApplyEnv(&cfg); err != nil {
		return cfg, err
	}
	if flagEndpoint != "" {
		cfg.Endpoint = flagEndpoint
	}
	if flagRegion != "" {
		cfg.Region = flagRegion
	}
	if flagBucket != "" {
		cfg.Bucket = flagBucket
	}
	if flagPrefix != "" {
		cfg.Prefix = flagPrefix
	}
	if flagKeyFile != "" {
		cfg.KeyFile = flagKeyFile
	}
	if flagSecretFile != "" {
		cfg.SecretFile = flagSecretFile
	}
	switch flagArchSuffix {
	case "true":
		cfg.ArchSuffix = true
	case "false":
		cfg.ArchSuffix = false
	case "":
		// inherit from file/env
	default:
		return cfg, fmt.Errorf("--arch-suffix must be true|false, got %q", flagArchSuffix)
	}
	return cfg, nil
}

// client builds a Garage-tuned S3 client from the resolved config.
func client() (*s3.Client, config.Config, error) {
	cfg, err := loadConfig()
	if err != nil {
		return nil, cfg, err
	}
	if err := cfg.Validate(); err != nil {
		return nil, cfg, err
	}
	key, secret, err := cfg.Credentials()
	if err != nil {
		return nil, cfg, err
	}
	c, err := s3.New(s3.Options{
		Endpoint: cfg.Endpoint,
		Region:   cfg.Region,
		Bucket:   cfg.Bucket,
		Prefix:   cfg.Prefix,
		KeyID:    key,
		Secret:   secret,
	})
	return c, cfg, err
}