cmd/uplink: use scopes to open (#2501)

What: Change cmd/uplink to use scopes

It moves the fields that will be subsumed by scopes into an explicit legacy section and hides their configuration flags.

Why: So that it can read scopes in from files and stuff
This commit is contained in:
Jeff Wendling 2019-08-05 13:01:20 -04:00 committed by JT Olio
parent 53017a0f03
commit 21a3bf89ee
31 changed files with 754 additions and 895 deletions

View File

@ -142,7 +142,8 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
overrides[kademliaBootstrapAddr.Name] = "127.0.0.1" + defaultServerAddr
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
return process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverrides(overrides))
}
func main() {

View File

@ -63,11 +63,11 @@ func cmdSetup(cmd *cobra.Command, args []string) error {
return errors.New("identity is missing")
}
overrides := map[string]interface{}{
"ca.cert-path": config.CA.CertPath,
"ca.key-path": config.CA.KeyPath,
"identity.cert-path": config.Identity.CertPath,
"identity.key-path": config.Identity.KeyPath,
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
return process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverrides(map[string]interface{}{
"ca.cert-path": config.CA.CertPath,
"ca.key-path": config.CA.KeyPath,
"identity.cert-path": config.Identity.CertPath,
"identity.key-path": config.Identity.KeyPath,
}))
}

View File

@ -27,7 +27,6 @@ import (
"storj.io/storj/pkg/process"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink"
"storj.io/storj/uplink/setup"
)
// GatewayFlags configuration flags
@ -107,7 +106,6 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
if err != nil {
return err
}
overrides[accessKeyFlag.Name] = accessKey
}
@ -117,24 +115,13 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
if err != nil {
return err
}
overrides[secretKeyFlag.Name] = secretKey
}
// override is required because the default value of Enc.KeyFilepath is ""
// and setting the value directly in setupCfg.Enc.KeyFiletpath will set the
// value in the config file but commented out.
encryptionKeyFilepath := setupCfg.Enc.KeyFilepath
if encryptionKeyFilepath == "" {
encryptionKeyFilepath = filepath.Join(setupDir, ".encryption.key")
overrides["enc.key-filepath"] = encryptionKeyFilepath
}
if setupCfg.NonInteractive {
return setupCfg.nonInteractive(cmd, setupDir, encryptionKeyFilepath, overrides)
return setupCfg.nonInteractive(cmd, setupDir, overrides)
}
return setupCfg.interactive(cmd, setupDir, encryptionKeyFilepath, overrides)
return setupCfg.interactive(cmd, setupDir, overrides)
}
func cmdRun(cmd *cobra.Command, args []string) (err error) {
@ -233,7 +220,7 @@ func (flags GatewayFlags) action(ctx context.Context, cliCtx *cli.Context) (err
// NewGateway creates a new minio Gateway
func (flags GatewayFlags) NewGateway(ctx context.Context) (gw minio.Gateway, err error) {
access, err := setup.LoadEncryptionAccess(ctx, flags.Enc)
scope, err := flags.GetScope()
if err != nil {
return nil, err
}
@ -245,7 +232,7 @@ func (flags GatewayFlags) NewGateway(ctx context.Context) (gw minio.Gateway, err
return miniogw.NewStorjGateway(
project,
access,
scope.EncryptionAccess,
storj.CipherSuite(flags.Enc.PathType),
flags.GetEncryptionParameters(),
flags.GetRedundancyScheme(),
@ -269,26 +256,24 @@ func (flags *GatewayFlags) newUplink(ctx context.Context) (*libuplink.Uplink, er
}
func (flags GatewayFlags) openProject(ctx context.Context) (*libuplink.Project, error) {
apiKey, err := libuplink.ParseAPIKey(flags.Client.APIKey)
scope, err := flags.GetScope()
if err != nil {
return nil, err
return nil, Error.Wrap(err)
}
uplk, err := flags.newUplink(ctx)
// TODO(jeff): this leaks the uplink and project :(
uplink, err := flags.newUplink(ctx)
if err != nil {
return nil, err
return nil, Error.Wrap(err)
}
return uplk.OpenProject(ctx, flags.Client.SatelliteAddr, apiKey)
project, err := uplink.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
if err != nil {
return nil, Error.Wrap(err)
}
return project, nil
}
// interactive creates the configuration of the gateway interactively.
//
// encryptionKeyFilepath should be set to the filepath indicated by the user or
// or to a default path whose directory tree exists.
func (flags GatewayFlags) interactive(
cmd *cobra.Command, setupDir string, encryptionKeyFilepath string, overrides map[string]interface{},
) error {
func (flags GatewayFlags) interactive(cmd *cobra.Command, setupDir string, overrides map[string]interface{}) error {
ctx := process.Ctx(cmd)
satelliteAddress, err := wizard.PromptForSatellite(cmd)
@ -311,13 +296,13 @@ func (flags GatewayFlags) interactive(
return Error.Wrap(err)
}
uplk, err := flags.newUplink(ctx)
uplink, err := flags.newUplink(ctx)
if err != nil {
return Error.Wrap(err)
}
defer func() { err = errs.Combine(err, uplk.Close()) }()
defer func() { err = errs.Combine(err, uplink.Close()) }()
project, err := uplk.OpenProject(ctx, satelliteAddress, apiKey)
project, err := uplink.OpenProject(ctx, satelliteAddress, apiKey)
if err != nil {
return Error.Wrap(err)
}
@ -328,63 +313,51 @@ func (flags GatewayFlags) interactive(
return Error.Wrap(err)
}
err = setup.SaveEncryptionKey(string(key[:]), encryptionKeyFilepath)
scopeData, err := (&libuplink.Scope{
SatelliteAddr: satelliteAddress,
APIKey: apiKey,
EncryptionAccess: libuplink.NewEncryptionAccessWithDefaultKey(*key),
}).Serialize()
if err != nil {
return Error.Wrap(err)
}
overrides["scope"] = scopeData
err = process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverrides(overrides),
process.SaveConfigRemovingDeprecated())
if err != nil {
return Error.Wrap(err)
}
overrides["satellite-addr"] = satelliteAddress
overrides["api-key"] = apiKeyString
overrides["enc.key-filepath"] = encryptionKeyFilepath
err = process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
if err != nil {
return nil
}
_, err = fmt.Printf(`
Your encryption key is saved to: %s
fmt.Println(`
Your S3 Gateway is configured and ready to use!
Some things to try next:
* Run 'gateway --help' to see the operations that can be performed
* See https://github.com/storj/docs/blob/master/S3-Gateway.md#using-the-aws-s3-commandline-interface for some example commands
`, encryptionKeyFilepath)
if err != nil {
return nil
}
* See https://github.com/storj/docs/blob/master/S3-Gateway.md#using-the-aws-s3-commandline-interface for some example commands`)
return nil
}
// nonInteractive creates the configuration of the gateway non-interactively.
//
// encryptionKeyFilepath should be set to the filepath indicated by the user or
// or to a default path whose directory tree exists.
func (flags GatewayFlags) nonInteractive(
cmd *cobra.Command, setupDir string, encryptionKeyFilepath string, overrides map[string]interface{},
) error {
if setupCfg.Enc.EncryptionKey != "" {
err := setup.SaveEncryptionKey(setupCfg.Enc.EncryptionKey, encryptionKeyFilepath)
if err != nil {
return Error.Wrap(err)
}
}
err := process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
func (flags GatewayFlags) nonInteractive(cmd *cobra.Command, setupDir string, overrides map[string]interface{}) error {
// ensure we're using the scope for the setup
scope, err := setupCfg.GetScope()
if err != nil {
return Error.Wrap(err)
return err
}
if setupCfg.Enc.EncryptionKey != "" {
_, _ = fmt.Printf("Your encryption key is saved to: %s\n", encryptionKeyFilepath)
scopeData, err := scope.Serialize()
if err != nil {
return err
}
overrides["scope"] = scopeData
return nil
return Error.Wrap(process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverrides(overrides),
process.SaveConfigRemovingDeprecated()))
}
func main() {

View File

@ -114,7 +114,7 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
return err
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), nil)
return process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"))
}
func configureTLS(certFile, keyFile string) (*tls.Config, error) {

View File

@ -172,7 +172,7 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
return err
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), nil)
return process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"))
}
func cmdQDiag(cmd *cobra.Command, args []string) (err error) {

View File

@ -27,8 +27,7 @@ import (
// StorageNodeFlags defines storage node configuration
type StorageNodeFlags struct {
EditConf bool `default:"false" help:"open config in default editor"`
SaveAllDefaults bool `default:"false" help:"save all default values to config.yaml file" setup:"true"`
EditConf bool `default:"false" help:"open config in default editor"`
storagenode.Config
}
@ -197,11 +196,7 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
}
configFile := filepath.Join(setupDir, "config.yaml")
if setupCfg.SaveAllDefaults {
err = process.SaveConfigWithAllDefaults(cmd.Flags(), configFile, overrides)
} else {
err = process.SaveConfig(cmd.Flags(), configFile, overrides)
}
err = process.SaveConfig(cmd, configFile, process.SaveConfigWithOverrides(overrides))
if err != nil {
return err
}

View File

@ -24,7 +24,9 @@ import (
"storj.io/storj/internal/dbutil/pgutil"
"storj.io/storj/internal/fpath"
"storj.io/storj/internal/processgroup"
"storj.io/storj/lib/uplink"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/storj"
)
const (
@ -34,6 +36,11 @@ const (
folderPermissions = 0744
)
var (
defaultAPIKeyData = "13YqgH45XZLg7nm6KsQ72QgXfjbDu2uhTaeSdMVP2A85QuANthM9K58ww5Y4nhMowrZDoqdA4Kyqt1ioQghQcm9fT5uR2drPHpFEqeb"
defaultAPIKey, _ = uplink.ParseAPIKey(defaultAPIKeyData)
)
const (
// The following values of peer class and endpoints are used
// to create a port with a consistent format for storj-sim services.
@ -176,7 +183,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
withCommon := func(dir string, all Arguments) Arguments {
common := []string{"--metrics.app-suffix", "sim", "--log.level", "debug", "--config-dir", dir}
if flags.IsDev {
common = append(common, "--dev")
common = append(common, "--defaults", "dev")
}
for command, args := range all {
all[command] = append(append(common, command), args...)
@ -311,21 +318,25 @@ func newNetwork(flags *Flags) (*Processes, error) {
Extra: []string{},
})
scopeData, err := (&uplink.Scope{
SatelliteAddr: satellite.Address,
APIKey: defaultAPIKey,
EncryptionAccess: uplink.NewEncryptionAccessWithDefaultKey(storj.Key{}),
}).Serialize()
if err != nil {
return nil, err
}
// gateway must wait for the corresponding satellite to start up
process.WaitForStart(satellite)
process.Arguments = withCommon(process.Directory, Arguments{
"setup": {
"--non-interactive",
"--enc.encryption-key=TestEncryptionKey",
"--scope", scopeData,
"--identity-dir", process.Directory,
"--satellite-addr", satellite.Address,
"--server.address", process.Address,
"--satellite-addr", satellite.Address,
"--rs.min-threshold", strconv.Itoa(1 * flags.StorageNodeCount / 5),
"--rs.repair-threshold", strconv.Itoa(2 * flags.StorageNodeCount / 5),
"--rs.success-threshold", strconv.Itoa(3 * flags.StorageNodeCount / 5),
@ -336,6 +347,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
"--debug.addr", net.JoinHostPort(host, port(gatewayPeer, i, debugHTTP)),
},
"run": {},
})
@ -356,12 +368,11 @@ func newNetwork(flags *Flags) (*Processes, error) {
// check if gateway config has an api key, if it's not
// create example project with key and add it to the config
// so that gateway can have access to the satellite
apiKey := vip.GetString("api-key")
if !flags.OnlyEnv && apiKey == "" {
if runScopeData := vip.GetString("scope"); !flags.OnlyEnv && runScopeData == scopeData {
var consoleAddress string
satelliteConfigErr := readConfigString(&consoleAddress, satellite.Directory, "console.address")
if satelliteConfigErr != nil {
return satelliteConfigErr
err := readConfigString(&consoleAddress, satellite.Directory, "console.address")
if err != nil {
return err
}
host := "http://" + consoleAddress
@ -372,19 +383,32 @@ func newNetwork(flags *Flags) (*Processes, error) {
// wait for console server to start
time.Sleep(3 * time.Second)
var apiKey string
if err := addExampleProjectWithKey(&apiKey, createRegistrationTokenAddress, consoleActivationAddress, consoleAPIAddress); err != nil {
return err
}
vip.Set("api-key", apiKey)
scope, err := uplink.ParseScope(runScopeData)
if err != nil {
return err
}
scope.APIKey, err = uplink.ParseAPIKey(apiKey)
if err != nil {
return err
}
scopeData, err := scope.Serialize()
if err != nil {
return err
}
vip.Set("scope", scopeData)
if err := vip.WriteConfig(); err != nil {
return err
}
}
if apiKey != "" {
process.Extra = append(process.Extra, "API_KEY="+apiKey)
if runScopeData := vip.GetString("scope"); runScopeData != scopeData {
process.Extra = append(process.Extra, "SCOPE="+runScopeData)
}
accessKey := vip.GetString("minio.access-key")
@ -429,8 +453,8 @@ func newNetwork(flags *Flags) (*Processes, error) {
"--kademlia.operator.email", fmt.Sprintf("storage%d@mail.test", i),
"--kademlia.operator.wallet", "0x0123456789012345678901234567890123456789",
"--storage2.monitor.minimum-disk-space", "10GB",
"--storage2.monitor.minimum-bandwidth", "10GB",
"--storage2.monitor.minimum-disk-space", "0",
"--storage2.monitor.minimum-bandwidth", "0",
"--server.extensions.revocation=false",
"--server.use-peer-ca-whitelist=false",
@ -461,7 +485,6 @@ func newNetwork(flags *Flags) (*Processes, error) {
}
process.ExecBefore["run"] = func(process *Process) error {
return readConfigString(&process.Address, process.Directory, "server.address")
}
}

View File

@ -19,7 +19,6 @@ import (
"storj.io/storj/internal/fpath"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/process"
"storj.io/storj/uplink/setup"
)
var (
@ -83,16 +82,10 @@ func upload(ctx context.Context, src fpath.FPath, dst fpath.FPath, showProgress
return fmt.Errorf("source cannot be a directory: %s", src)
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket())
if err != nil {
return err
}
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket(), access)
if err != nil {
return err
}
defer closeProjectAndBucket(project, bucket)
reader := io.Reader(file)
@ -136,16 +129,10 @@ func download(ctx context.Context, src fpath.FPath, dst fpath.FPath, showProgres
return fmt.Errorf("destination must be local path: %s", dst)
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
project, bucket, err := cfg.GetProjectAndBucket(ctx, src.Bucket())
if err != nil {
return err
}
project, bucket, err := cfg.GetProjectAndBucket(ctx, src.Bucket(), access)
if err != nil {
return err
}
defer closeProjectAndBucket(project, bucket)
object, err := bucket.OpenObject(ctx, src.Path())
@ -215,16 +202,10 @@ func copyObject(ctx context.Context, src fpath.FPath, dst fpath.FPath) (err erro
return fmt.Errorf("destination must be Storj URL: %s", dst)
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket())
if err != nil {
return err
}
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket(), access)
if err != nil {
return err
}
defer closeProjectAndBucket(project, bucket)
object, err := bucket.OpenObject(ctx, src.Path())

View File

@ -14,7 +14,6 @@ import (
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/process"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink/setup"
)
var (
@ -43,7 +42,7 @@ func list(cmd *cobra.Command, args []string) error {
}
}()
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
scope, err := cfg.GetScope()
if err != nil {
return err
}
@ -59,11 +58,10 @@ func list(cmd *cobra.Command, args []string) error {
return fmt.Errorf("No bucket specified, use format sj://bucket/")
}
bucket, err := project.OpenBucket(ctx, src.Bucket(), access)
bucket, err := project.OpenBucket(ctx, src.Bucket(), scope.EncryptionAccess)
if err != nil {
return err
}
defer func() {
if err := bucket.Close(); err != nil {
fmt.Printf("error closing bucket: %+v\n", err)
@ -71,7 +69,6 @@ func list(cmd *cobra.Command, args []string) error {
}()
err = listFiles(ctx, bucket, src, false)
return convertError(err, src)
}
@ -92,7 +89,7 @@ func list(cmd *cobra.Command, args []string) error {
for _, bucket := range list.Items {
fmt.Println("BKT", formatTime(bucket.Created), bucket.Name)
if *recursiveFlag {
if err := listFilesFromBucket(ctx, project, bucket.Name, access); err != nil {
if err := listFilesFromBucket(ctx, project, bucket.Name, scope.EncryptionAccess); err != nil {
return err
}
}
@ -122,7 +119,6 @@ func listFilesFromBucket(ctx context.Context, project *libuplink.Project, bucket
if err != nil {
return err
}
defer func() {
if err := bucket.Close(); err != nil {
fmt.Printf("error closing bucket: %+v\n", err)

View File

@ -11,7 +11,6 @@ import (
"storj.io/storj/internal/fpath"
"storj.io/storj/pkg/process"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink/setup"
)
func init() {
@ -42,16 +41,10 @@ func deleteBucket(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Nested buckets not supported, use format sj://bucket/")
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
if err != nil {
return err
}
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket(), access)
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket())
if err != nil {
return convertError(err, dst)
}
defer closeProjectAndBucket(project, bucket)
list, err := bucket.ListObjects(ctx, &storj.ListOptions{Direction: storj.After, Recursive: true, Limit: 1})

View File

@ -10,7 +10,6 @@ import (
"storj.io/storj/internal/fpath"
"storj.io/storj/pkg/process"
"storj.io/storj/uplink/setup"
)
func init() {
@ -37,16 +36,10 @@ func deleteObject(cmd *cobra.Command, args []string) error {
return fmt.Errorf("No bucket specified, use format sj://bucket/")
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
if err != nil {
return err
}
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket(), access)
project, bucket, err := cfg.GetProjectAndBucket(ctx, dst.Bucket())
if err != nil {
return convertError(err, dst)
}
defer closeProjectAndBucket(project, bucket)
err = bucket.DeleteObject(ctx, dst.Path())

View File

@ -63,7 +63,6 @@ func addCmd(cmd *cobra.Command, root *cobra.Command) *cobra.Command {
root.AddCommand(cmd)
defaultConfDir := fpath.ApplicationDir("storj", "uplink")
confDirParam := cfgstruct.FindConfigDirParam()
if confDirParam != "" {
defaultConfDir = confDirParam
@ -92,13 +91,13 @@ func (cliCfg *UplinkFlags) NewUplink(ctx context.Context) (*libuplink.Uplink, er
}
// GetProject returns a *libuplink.Project for interacting with a specific project
func (cliCfg *UplinkFlags) GetProject(ctx context.Context) (*libuplink.Project, error) {
err := version.CheckProcessVersion(ctx, zap.L(), cliCfg.Version, version.Build, "Uplink")
func (cliCfg *UplinkFlags) GetProject(ctx context.Context) (_ *libuplink.Project, err error) {
err = version.CheckProcessVersion(ctx, zap.L(), cliCfg.Version, version.Build, "Uplink")
if err != nil {
return nil, err
}
apiKey, err := libuplink.ParseAPIKey(cliCfg.Client.APIKey)
scope, err := cliCfg.GetScope()
if err != nil {
return nil, err
}
@ -107,24 +106,40 @@ func (cliCfg *UplinkFlags) GetProject(ctx context.Context) (*libuplink.Project,
if err != nil {
return nil, err
}
project, err := uplk.OpenProject(ctx, cliCfg.Client.SatelliteAddr, apiKey)
if err != nil {
if err := uplk.Close(); err != nil {
fmt.Printf("error closing uplink: %+v\n", err)
defer func() {
if err != nil {
if err := uplk.Close(); err != nil {
fmt.Printf("error closing uplink: %+v\n", err)
}
}
}
}()
return project, err
return uplk.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
}
// GetProjectAndBucket returns a *libuplink.Bucket for interacting with a specific project's bucket
func (cliCfg *UplinkFlags) GetProjectAndBucket(ctx context.Context, bucketName string, access *libuplink.EncryptionAccess) (project *libuplink.Project, bucket *libuplink.Bucket, err error) {
project, err = cliCfg.GetProject(ctx)
func (cliCfg *UplinkFlags) GetProjectAndBucket(ctx context.Context, bucketName string) (project *libuplink.Project, bucket *libuplink.Bucket, err error) {
scope, err := cliCfg.GetScope()
if err != nil {
return project, bucket, err
return nil, nil, err
}
uplk, err := cliCfg.NewUplink(ctx)
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
if err := uplk.Close(); err != nil {
fmt.Printf("error closing uplink: %+v\n", err)
}
}
}()
project, err = uplk.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
if err := project.Close(); err != nil {
@ -133,11 +148,7 @@ func (cliCfg *UplinkFlags) GetProjectAndBucket(ctx context.Context, bucketName s
}
}()
bucket, err = project.OpenBucket(ctx, bucketName, access)
if err != nil {
return project, bucket, err
}
bucket, err = project.OpenBucket(ctx, bucketName, scope.EncryptionAccess)
return project, bucket, err
}

View File

@ -18,7 +18,6 @@ import (
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/cfgstruct"
"storj.io/storj/pkg/process"
"storj.io/storj/uplink/setup"
)
var (
@ -37,12 +36,6 @@ func init() {
}
func cmdSetup(cmd *cobra.Command, args []string) (err error) {
// Ensure use the default port if the user only specifies a host.
err = ApplyDefaultHostAndPortToAddrFlag(cmd, "satellite-addr")
if err != nil {
return err
}
setupDir, err := filepath.Abs(confDir)
if err != nil {
return err
@ -58,61 +51,59 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
return err
}
// override is required because the default value of Enc.KeyFilepath is ""
// and setting the value directly in setupCfg.Enc.KeyFiletpathon will set the
// value in the config file but commented out.
usedEncryptionKeyFilepath := setupCfg.Enc.KeyFilepath
if usedEncryptionKeyFilepath == "" {
usedEncryptionKeyFilepath = filepath.Join(setupDir, ".encryption.key")
}
if setupCfg.NonInteractive {
return cmdSetupNonInteractive(cmd, setupDir, usedEncryptionKeyFilepath)
return cmdSetupNonInteractive(cmd, setupDir)
}
return cmdSetupInteractive(cmd, setupDir, usedEncryptionKeyFilepath)
return cmdSetupInteractive(cmd, setupDir)
}
// cmdSetupNonInteractive sets up uplink non-interactively.
//
// encryptionKeyFilepath should be set to the filepath indicated by the user or
// or to a default path whose directory tree exists.
func cmdSetupNonInteractive(cmd *cobra.Command, setupDir string, encryptionKeyFilepath string) error {
if setupCfg.Enc.EncryptionKey != "" {
err := setup.SaveEncryptionKey(setupCfg.Enc.EncryptionKey, encryptionKeyFilepath)
if err != nil {
return err
}
}
override := map[string]interface{}{
"enc.key-filepath": encryptionKeyFilepath,
}
err := process.SaveConfigWithAllDefaults(
cmd.Flags(), filepath.Join(setupDir, process.DefaultCfgFilename), override)
func cmdSetupNonInteractive(cmd *cobra.Command, setupDir string) error {
// ensure we're using the scope for the setup
scope, err := setupCfg.GetScope()
if err != nil {
return err
}
if setupCfg.Enc.EncryptionKey != "" {
_, _ = fmt.Printf("Your encryption key is saved to: %s\n", encryptionKeyFilepath)
// apply helpful default host and port to the address
vip, err := process.Viper(cmd)
if err != nil {
return err
}
scope.SatelliteAddr, err = ApplyDefaultHostAndPortToAddr(
scope.SatelliteAddr, vip.GetString("satellite-addr"))
if err != nil {
return err
}
return nil
scopeData, err := scope.Serialize()
if err != nil {
return err
}
return Error.Wrap(process.SaveConfig(cmd, filepath.Join(setupDir, process.DefaultCfgFilename),
process.SaveConfigWithOverride("scope", scopeData),
process.SaveConfigRemovingDeprecated()))
}
// cmdSetupInteractive sets up uplink interactively.
//
// encryptionKeyFilepath should be set to the filepath indicated by the user or
// or to a default path whose directory tree exists.
func cmdSetupInteractive(cmd *cobra.Command, setupDir string, encryptionKeyFilepath string) error {
func cmdSetupInteractive(cmd *cobra.Command, setupDir string) error {
ctx := process.Ctx(cmd)
satelliteAddress, err := wizard.PromptForSatellite(cmd)
if err != nil {
return Error.Wrap(err)
}
// apply helpful default host and port to the address
vip, err := process.Viper(cmd)
if err != nil {
return err
}
satelliteAddress, err = ApplyDefaultHostAndPortToAddr(
satelliteAddress, vip.GetString("satellite-addr"))
if err != nil {
return Error.Wrap(err)
}
apiKeyString, err := wizard.PromptForAPIKey()
if err != nil {
@ -129,13 +120,13 @@ func cmdSetupInteractive(cmd *cobra.Command, setupDir string, encryptionKeyFilep
return Error.Wrap(err)
}
uplk, err := setupCfg.NewUplink(ctx)
uplink, err := libuplink.NewUplink(ctx, nil)
if err != nil {
return Error.Wrap(err)
}
defer func() { err = errs.Combine(err, uplk.Close()) }()
defer func() { err = errs.Combine(err, uplink.Close()) }()
project, err := uplk.OpenProject(ctx, satelliteAddress, apiKey)
project, err := uplink.OpenProject(ctx, satelliteAddress, apiKey)
if err != nil {
return Error.Wrap(err)
}
@ -146,61 +137,36 @@ func cmdSetupInteractive(cmd *cobra.Command, setupDir string, encryptionKeyFilep
return Error.Wrap(err)
}
err = setup.SaveEncryptionKey(string(key[:]), encryptionKeyFilepath)
scopeData, err := (&libuplink.Scope{
SatelliteAddr: satelliteAddress,
APIKey: apiKey,
EncryptionAccess: libuplink.NewEncryptionAccessWithDefaultKey(*key),
}).Serialize()
if err != nil {
return err
return Error.Wrap(err)
}
var override = map[string]interface{}{
"api-key": apiKeyString,
"satellite-addr": satelliteAddress,
"enc.key-filepath": encryptionKeyFilepath,
}
err = process.SaveConfigWithAllDefaults(
cmd.Flags(), filepath.Join(setupDir, process.DefaultCfgFilename), override)
err = process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverride("scope", scopeData),
process.SaveConfigRemovingDeprecated())
if err != nil {
return nil
return Error.Wrap(err)
}
// if there is an error with this we cannot do that much and the setup process
// has ended OK, so we ignore it.
_, _ = fmt.Printf(`
Your encryption key is saved to: %s
fmt.Println(`
Your Uplink CLI is configured and ready to use!
Some things to try next:
* Run 'uplink --help' to see the operations that can be performed
* See https://github.com/storj/docs/blob/master/Uplink-CLI.md#usage for some example commands
`, encryptionKeyFilepath)
* See https://github.com/storj/docs/blob/master/Uplink-CLI.md#usage for some example commands`)
return nil
}
// ApplyDefaultHostAndPortToAddrFlag applies the default host and/or port if either is missing in the specified flag name.
func ApplyDefaultHostAndPortToAddrFlag(cmd *cobra.Command, flagName string) error {
flag := cmd.Flags().Lookup(flagName)
if flag == nil {
// No flag found for us to handle.
return nil
}
address, err := ApplyDefaultHostAndPortToAddr(flag.Value.String(), flag.DefValue)
if err != nil {
return Error.Wrap(err)
}
if flag.Value.String() == address {
// Don't trip the flag set bit
return nil
}
return Error.Wrap(flag.Value.Set(address))
}
// ApplyDefaultHostAndPortToAddr applies the default host and/or port if either is missing in the specified address.
func ApplyDefaultHostAndPortToAddr(address, defaultAddress string) (string, error) {
defaultHost, defaultPort, err := net.SplitHostPort(defaultAddress)

View File

@ -6,113 +6,44 @@ package cmd_test
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"storj.io/storj/cmd/uplink/cmd"
)
func TestDefaultHostAndPortAppliedToSatelliteAddrWithNoHostOrPort(t *testing.T) {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Create an uplink config file",
RunE: nil,
Annotations: map[string]string{"type": "setup"},
func TestApplyDefaultHostAndPortToAddr(t *testing.T) {
{
got, err := cmd.ApplyDefaultHostAndPortToAddr("", "localhost:7777")
assert.NoError(t, err)
assert.Equal(t, "localhost:7777", got,
"satellite-addr should contain default port when no port specified")
}
flagName := "satellite-addr"
defaultValue := "localhost:7777"
setupCmd.Flags().String(flagName, defaultValue, "")
err := setupCmd.Flags().Set(flagName, "")
assert.NoError(t, err)
err = cmd.ApplyDefaultHostAndPortToAddrFlag(setupCmd, flagName)
assert.NoError(t, err)
assert.Equal(t, "localhost:7777", setupCmd.Flags().Lookup("satellite-addr").Value.String(),
"satellite-addr should contain default port when no port specified")
}
func TestDefaultPortAppliedToSatelliteAddrWithNoPort(t *testing.T) {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Create an uplink config file",
RunE: nil,
Annotations: map[string]string{"type": "setup"},
{
got, err := cmd.ApplyDefaultHostAndPortToAddr("ahost", "localhost:7777")
assert.NoError(t, err)
assert.Equal(t, "ahost:7777", got,
"satellite-addr should contain default port when no port specified")
}
flagName := "satellite-addr"
defaultValue := "localhost:7777"
setupCmd.Flags().String(flagName, defaultValue, "")
err := setupCmd.Flags().Set(flagName, "ahost")
assert.NoError(t, err)
err = cmd.ApplyDefaultHostAndPortToAddrFlag(setupCmd, flagName)
assert.NoError(t, err)
assert.Equal(t, "ahost:7777", setupCmd.Flags().Lookup("satellite-addr").Value.String(),
"satellite-addr should contain default port when no port specified")
}
func TestNoDefaultPortAppliedToSatelliteAddrWithPort(t *testing.T) {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Create an uplink config file",
RunE: nil,
Annotations: map[string]string{"type": "setup"},
{
got, err := cmd.ApplyDefaultHostAndPortToAddr("ahost:7778", "localhost:7777")
assert.NoError(t, err)
assert.Equal(t, "ahost:7778", got,
"satellite-addr should contain default port when no port specified")
}
flagName := "satellite-addr"
defaultValue := "localhost:7777"
setupCmd.Flags().String(flagName, defaultValue, "")
err := setupCmd.Flags().Set(flagName, "ahost:7778")
assert.NoError(t, err)
err = cmd.ApplyDefaultHostAndPortToAddrFlag(setupCmd, flagName)
assert.NoError(t, err)
assert.Equal(t, "ahost:7778", setupCmd.Flags().Lookup(flagName).Value.String(),
"satellite-addr should contain default port when no port specified")
}
func TestDefaultHostAppliedToSatelliteAddrWithNoHost(t *testing.T) {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Create an uplink config file",
RunE: nil,
Annotations: map[string]string{"type": "setup"},
{
got, err := cmd.ApplyDefaultHostAndPortToAddr(":7778", "localhost:7777")
assert.NoError(t, err)
assert.Equal(t, "localhost:7778", got,
"satellite-addr should contain default port when no port specified")
}
flagName := "satellite-addr"
defaultValue := "localhost:7777"
setupCmd.Flags().String(flagName, defaultValue, "")
err := setupCmd.Flags().Set(flagName, ":7778")
assert.NoError(t, err)
err = cmd.ApplyDefaultHostAndPortToAddrFlag(setupCmd, flagName)
assert.NoError(t, err)
assert.Equal(t, "localhost:7778", setupCmd.Flags().Lookup("satellite-addr").Value.String(),
"satellite-addr should contain default port when no port specified")
}
func TestDefaultPortAppliedToSatelliteAddrWithPortColonButNoPort(t *testing.T) {
setupCmd := &cobra.Command{
Use: "setup",
Short: "Create an uplink config file",
RunE: nil,
Annotations: map[string]string{"type": "setup"},
{
got, err := cmd.ApplyDefaultHostAndPortToAddr("ahost:", "localhost:7777")
assert.NoError(t, err)
assert.Equal(t, "ahost:7777", got,
"satellite-addr should contain default port when no port specified")
}
flagName := "satellite-addr"
defaultValue := "localhost:7777"
setupCmd.Flags().String(flagName, defaultValue, "")
err := setupCmd.Flags().Set(flagName, "ahost:")
assert.NoError(t, err)
err = cmd.ApplyDefaultHostAndPortToAddrFlag(setupCmd, flagName)
assert.NoError(t, err)
assert.Equal(t, "ahost:7777", setupCmd.Flags().Lookup("satellite-addr").Value.String(),
"satellite-addr should contain default port when no port specified")
}

View File

@ -12,11 +12,11 @@ import (
"github.com/zeebo/errs"
"storj.io/storj/internal/fpath"
"storj.io/storj/lib/uplink"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/cfgstruct"
"storj.io/storj/pkg/macaroon"
"storj.io/storj/pkg/process"
"storj.io/storj/uplink/setup"
"storj.io/storj/uplink"
)
var shareCfg struct {
@ -29,19 +29,29 @@ var shareCfg struct {
NotBefore string `help:"disallow access before this time"`
NotAfter string `help:"disallow access after this time"`
AllowedPathPrefix []string `help:"whitelist of bucket path prefixes to require"`
// Share requires information about the current scope
uplink.ScopeConfig
}
func init() {
// sadly, we have to use addCmd so that it adds the cfg struct to the flags
// so that we can open projects and buckets. that pulls in so many unnecessary
// flags which makes figuring out the share command really hard. oh well.
shareCmd := addCmd(&cobra.Command{
// We skip the use of addCmd here because we only want the configuration options listed
// above, and addCmd adds a whole lot more than we want.
shareCmd := &cobra.Command{
Use: "share",
Short: "Creates a possibly restricted api key",
RunE: shareMain,
}, RootCmd)
}
RootCmd.AddCommand(shareCmd)
process.Bind(shareCmd, &shareCfg)
defaultConfDir := fpath.ApplicationDir("storj", "uplink")
confDirParam := cfgstruct.FindConfigDirParam()
if confDirParam != "" {
defaultConfDir = confDirParam
}
process.Bind(shareCmd, &shareCfg, defaults, cfgstruct.ConfDir(defaultConfDir))
}
const shareISO8601 = "2006-01-02T15:04:05-0700"
@ -68,7 +78,6 @@ func parseHumanDate(date string, now time.Time) (*time.Time, error) {
// shareMain is the function executed when shareCmd is called
func shareMain(cmd *cobra.Command, args []string) (err error) {
ctx := process.Ctx(cmd)
now := time.Now()
notBefore, err := parseHumanDate(shareCfg.NotBefore, now)
@ -96,15 +105,11 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
})
}
key, err := libuplink.ParseAPIKey(cfg.Client.APIKey)
if err != nil {
return err
}
access, err := setup.LoadEncryptionAccess(ctx, cfg.Enc)
scope, err := shareCfg.GetScope()
if err != nil {
return err
}
key, access := scope.APIKey, scope.EncryptionAccess
if len(restrictions) > 0 {
key, access, err = access.Restrict(key, restrictions...)
@ -152,13 +157,13 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
return err
}
scope := &uplink.Scope{
SatelliteAddr: cfg.Client.SatelliteAddr,
newScope := &libuplink.Scope{
SatelliteAddr: scope.SatelliteAddr,
APIKey: key,
EncryptionAccess: access,
}
scopeData, err := scope.Serialize()
scopeData, err := newScope.Serialize()
if err != nil {
return err
}

View File

@ -88,7 +88,8 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
overrides[serverAddress.Name] = defaultServerAddr
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
return process.SaveConfig(cmd, filepath.Join(setupDir, "config.yaml"),
process.SaveConfigWithOverrides(overrides))
}
func main() {

12
go.mod
View File

@ -11,7 +11,6 @@ require (
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/minio/minio v0.0.0-20180508161510-54cd29b51c38
github.com/mitchellh/mapstructure v1.1.1 // indirect
github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad
)
@ -25,7 +24,6 @@ require (
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/boltdb/bolt v1.3.1
github.com/cheggaaa/pb v1.0.5-0.20160713104425-73ae1d68fe0b
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/djherbis/atime v1.0.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/eapache/go-resiliency v1.1.0 // indirect
@ -87,16 +85,16 @@ require (
github.com/pierrec/lz4 v2.0.5+incompatible // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/pkg/profile v1.2.1 // indirect
github.com/prometheus/client_golang v0.9.3 // indirect
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e // indirect
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
github.com/rs/cors v1.5.0 // indirect
github.com/sirupsen/logrus v1.3.0 // indirect
github.com/skyrings/skyring-common v0.0.0-20160929130248-d1c0bb1cbd5e
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 // indirect
github.com/spf13/cast v1.3.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.2.1
github.com/spf13/viper v1.4.0
github.com/streadway/amqp v0.0.0-20180806233856-70e15c650864 // indirect
github.com/stretchr/testify v1.3.0
github.com/stripe/stripe-go v60.17.0+incompatible
@ -109,16 +107,12 @@ require (
github.com/zeebo/float16 v0.1.0 // indirect
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
github.com/zeebo/structs v1.0.2
go.etcd.io/bbolt v1.3.2 // indirect
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190730183949-1393eb018365
golang.org/x/text v0.3.2 // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
golang.org/x/tools v0.0.0-20190614152001-1edc8e83c897
google.golang.org/appengine v1.6.0 // indirect
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610 // indirect
@ -127,5 +121,5 @@ require (
gopkg.in/cheggaaa/pb.v1 v1.0.25 // indirect
gopkg.in/olivere/elastic.v5 v5.0.76 // indirect
gopkg.in/spacemonkeygo/monkit.v2 v2.0.0-20190612171030-cf5a9e6f8fd2
gopkg.in/yaml.v2 v2.2.2 // indirect
gopkg.in/yaml.v2 v2.2.2
)

48
go.sum
View File

@ -20,6 +20,7 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMw
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v0.0.0-20180911162847-3657542c8629 h1:gLoh8jzwIxdisBnHiWRIuReqtH9cpslSE2564UWXun0=
github.com/alicebob/miniredis v0.0.0-20180911162847-3657542c8629/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/aws/aws-sdk-go v1.15.34/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
@ -40,6 +41,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudfoundry/gosigar v1.1.0 h1:V/dVCzhKOdIU3WRB5inQU20s4yIgL9Dxx/Mhi0SF8eM=
github.com/cloudfoundry/gosigar v1.1.0/go.mod h1:3qLfc2GlfmwOx2+ZDaRGH3Y9fwQ0sQeaAleo2GV5pH0=
github.com/cockroachdb/cockroach-go v0.0.0-20180212155653-59c0560478b7/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
@ -92,6 +98,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsouza/fake-gcs-server v1.2.0/go.mod h1:rM69NBSmfAkTlKDhzXC41OeX9lQxQXnkimkiGWDU1Ek=
github.com/garyburd/redigo v1.0.1-0.20170216214944-0d253a66e6e1 h1:YmyuMm99D7kezPc0ZVWYnaUIWfMKR81lVVXttKTnDbw=
github.com/garyburd/redigo v1.0.1-0.20170216214944-0d253a66e6e1/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4=
github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
@ -111,6 +118,7 @@ github.com/golang-migrate/migrate/v3 v3.5.2 h1:SUWSv6PD8Lr2TGx1lmVW7W2lRoQiVny3s
github.com/golang-migrate/migrate/v3 v3.5.2/go.mod h1:QDa9JH6Bdwbi+1jIEpOIWnTNoYRbtaVgByErXU0844E=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -127,6 +135,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
@ -153,8 +162,12 @@ github.com/gorilla/rpc v1.1.0 h1:marKfvVP0Gpd/jHlVBKCQ8RAoUPdX7K1Nuh6l1BNh7A=
github.com/gorilla/rpc v1.1.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/graphql-go/graphql v0.7.9-0.20190403165646-199d20bbfed7 h1:E45QFM7IqRdFnuyFk8GSamb42EckUSyJ55rtVB/w8VQ=
github.com/graphql-go/graphql v0.7.9-0.20190403165646-199d20bbfed7/go.mod h1:k6yrAYQaSP59DC5UVxbgxESlmVyojThKdORUqGDGmrI=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@ -182,6 +195,7 @@ github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98j
github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/go-luar v0.0.0-20170419063437-0786921db8c0 h1:UyVaeqfY1fLPMt1iUTaWsxUNxYAzZVyK+7G+a3sRfhk=
@ -247,10 +261,8 @@ github.com/minio/sio v0.0.0-20180327104954-6a41828a60f0 h1:ys4bbOlPvaUBlA0byjm6T
github.com/minio/sio v0.0.0-20180327104954-6a41828a60f0/go.mod h1:PDJGYr8GXjiOTIst0hQMOSK5FdXLwObr2cGbiMddDPc=
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff h1:jM4Eo4qMmmcqePS3u6X2lcEELtVuXWkWJIS/pRI3oSk=
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.1 h1:0fcGQkeJPHl7DauilpdNG27ZxXHDSg+rbbTpfpniZd8=
github.com/mitchellh/mapstructure v1.1.1/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/gnatsd v1.3.0 h1:+5d80klu3QaJgNbdavVBjWJP7cHd11U2CLnRTFM9ICI=
github.com/nats-io/gnatsd v1.3.0/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ=
@ -317,6 +329,7 @@ github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e/go.mod h1:TjEm7z
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rs/cors v1.5.0 h1:dgSHE6+ia18arGOTIYQKKGWLvEbGvmbNE6NfxhoNHUY=
github.com/rs/cors v1.5.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad h1:EqOdoSJGI7CsBQczPcIgmpm3hJE7X8Hj3jrgI002whs=
@ -333,6 +346,7 @@ github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 h1:xHQewZjohU9/wUsyC99navCjQDNHtTgUOM/J1jAbzfw=
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1/go.mod h1:7NL9UAYQnRM5iKHUCld3tf02fKb5Dft+41+VckASUy0=
github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a h1:8+cCjxhToanKmxLIbuyBNe2EnpgwhiivsIaRJstDRFA=
@ -342,20 +356,16 @@ github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M=
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/streadway/amqp v0.0.0-20180806233856-70e15c650864 h1:Oj3PUEs+OUSYUpn35O+BE/ivHGirKixA3+vqA0Atu9A=
github.com/streadway/amqp v0.0.0-20180806233856-70e15c650864/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -371,8 +381,12 @@ github.com/tidwall/gjson v1.1.3 h1:u4mspaByxY+Qk4U1QYYVzGFI8qxN/3jtEV0ZDb2vRic=
github.com/tidwall/gjson v1.1.3/go.mod h1:c/nTNbUr0E0OrXEhq1pwa8iEgc2DOt4ZZqAt1HtCkPA=
github.com/tidwall/match v0.0.0-20171002075945-1731857f09b1 h1:pWIN9LOlFRCJFqWIOEbHLvY0WWJddsjH2FQ6N0HKZdU=
github.com/tidwall/match v0.0.0-20171002075945-1731857f09b1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b h1:dLkqBELopfQNhe8S9ucnSf+HhiUCgK/hPIjVG0f9GlY=
github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b/go.mod h1:5oyMAv4hrBEKqBwORFsiqIrCNCmL2qcZLQTdJLYeYIc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/gopher-lua v0.0.0-20180918061612-799fa34954fb h1:Jmfk7z2f/+gxVFAgPsJMuczO1uEIxZy6wytTdeZ49lg=
github.com/yuin/gopher-lua v0.0.0-20180918061612-799fa34954fb/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
github.com/zeebo/admission v0.0.0-20180821192747-f24f2a94a40c h1:WoYvMZp+keiJz+ZogLAhwsUZvWe81W+mCnpfdgEUOl4=
@ -393,8 +407,8 @@ go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
go.opencensus.io v0.16.0/go.mod h1:0TeCCqcQSLNZtiq/62+vUzqwnjqF5el6hjmuZaFtyNk=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
@ -415,9 +429,13 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
@ -433,7 +451,6 @@ golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87 h1:GqwDwfvIpC33dK9bA1fD+JiDU
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -479,6 +496,9 @@ google.golang.org/grpc v1.15.0 h1:Az/KuahOM4NAidTEuJCv/RonAA7rYsTPkqXVjr+8OOw=
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/Shopify/sarama.v1 v1.18.0 h1:f9aTXuIEFEjVvLG9p+kMSk01dMfFumHsySRk1okTdqU=
@ -500,12 +520,14 @@ gopkg.in/ini.v1 v1.38.2 h1:dGcbywv4RufeGeiMycPT/plKB5FtmLKLnWKwBiLhUA4=
gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/olivere/elastic.v5 v5.0.76 h1:A6W7X4yLPQDINHiYAqIwqev+rD5hIQ4G0e1d5H//VXk=
gopkg.in/olivere/elastic.v5 v5.0.76/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/spacemonkeygo/monkit.v2 v2.0.0-20180827161543-6ebf5a752f9b h1:zkhw+LuPvld5/ZwIlpYzd4DX/r0KxTvhEpo+2lcrp9k=
gopkg.in/spacemonkeygo/monkit.v2 v2.0.0-20180827161543-6ebf5a752f9b/go.mod h1:6UQdi0rNB4YDNwVYP8fJNmjLNaU8MA84WCdXStx3Spo=
gopkg.in/spacemonkeygo/monkit.v2 v2.0.0-20190612171030-cf5a9e6f8fd2 h1:Hzf9SARsnWv8oWl9TWmQ3vo3lSqOMByumL6aJ7ehw7g=
gopkg.in/spacemonkeygo/monkit.v2 v2.0.0-20190612171030-cf5a9e6f8fd2/go.mod h1:6UQdi0rNB4YDNwVYP8fJNmjLNaU8MA84WCdXStx3Spo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

View File

@ -28,7 +28,6 @@ import (
"storj.io/storj/uplink"
"storj.io/storj/uplink/metainfo"
"storj.io/storj/uplink/piecestore"
"storj.io/storj/uplink/setup"
)
// Uplink is a general purpose
@ -295,8 +294,30 @@ func (client *Uplink) CreateBucket(ctx context.Context, satellite *satellite.Pee
// GetConfig returns a default config for a given satellite.
func (client *Uplink) GetConfig(satellite *satellite.Peer) uplink.Config {
config := getDefaultConfig()
config.Client.SatelliteAddr = satellite.Addr()
config.Client.APIKey = client.APIKey[satellite.ID()]
apiKey, err := libuplink.ParseAPIKey(client.APIKey[satellite.ID()])
if err != nil {
panic(err)
}
encAccess := libuplink.NewEncryptionAccess()
encAccess.SetDefaultKey(storj.Key{})
scopeData, err := (&libuplink.Scope{
SatelliteAddr: satellite.Addr(),
APIKey: apiKey,
EncryptionAccess: encAccess,
}).Serialize()
if err != nil {
panic(err)
}
config.Scope = scopeData
// Support some legacy stuff
config.Legacy.Client.APIKey = apiKey.Serialize()
config.Legacy.Client.SatelliteAddr = satellite.Addr()
config.Client.RequestTimeout = 10 * time.Second
config.Client.DialTimeout = 10 * time.Second
@ -350,13 +371,12 @@ func (client *Uplink) GetProject(ctx context.Context, satellite *satellite.Peer)
}
defer func() { err = errs.Combine(err, testLibuplink.Close()) }()
clientAPIKey := client.APIKey[satellite.ID()]
key, err := libuplink.ParseAPIKey(clientAPIKey)
scope, err := client.GetConfig(satellite).GetScope()
if err != nil {
return nil, err
}
project, err := testLibuplink.OpenProject(ctx, satellite.Addr(), key)
project, err := testLibuplink.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
if err != nil {
return nil, err
}
@ -369,14 +389,13 @@ func (client *Uplink) GetProjectAndBucket(ctx context.Context, satellite *satell
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
err = errs.Combine(err, project.Close())
}
}()
access, err := setup.LoadEncryptionAccess(ctx, clientCfg.Enc)
scope, err := client.GetConfig(satellite).GetScope()
if err != nil {
return nil, nil, err
}
@ -394,11 +413,10 @@ func (client *Uplink) GetProjectAndBucket(ctx context.Context, satellite *satell
}
}
bucket, err := project.OpenBucket(ctx, bucketName, access)
bucket, err := project.OpenBucket(ctx, bucketName, scope.EncryptionAccess)
if err != nil {
return nil, nil, err
}
return project, bucket, nil
}

View File

@ -14,9 +14,7 @@ import (
"storj.io/storj/internal/testplanet"
"storj.io/storj/lib/uplink"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/macaroon"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink/setup"
)
func TestProjectListBuckets(t *testing.T) {
@ -29,17 +27,14 @@ func TestProjectListBuckets(t *testing.T) {
cfg.Volatile.Log = zaptest.NewLogger(t)
cfg.Volatile.TLS.SkipPeerCAWhitelist = true
satelliteAddr := planet.Satellites[0].Local().Address.Address
apiKey := planet.Uplinks[0].APIKey[planet.Satellites[0].ID()]
scope, err := planet.Uplinks[0].GetConfig(planet.Satellites[0]).GetScope()
require.NoError(t, err)
ul, err := uplink.NewUplink(ctx, &cfg)
require.NoError(t, err)
defer ctx.Check(ul.Close)
key, err := uplink.ParseAPIKey(apiKey)
require.NoError(t, err)
p, err := ul.OpenProject(ctx, satelliteAddr, key)
p, err := ul.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
require.NoError(t, err)
// create 6 test buckets
@ -73,28 +68,13 @@ func TestProjectListBuckets(t *testing.T) {
require.False(t, result.More)
// List with restrictions
restriction := libuplink.EncryptionRestriction{
Bucket: "test0",
}
access, err := setup.LoadEncryptionAccess(ctx,
planet.Uplinks[0].GetConfig(planet.Satellites[0]).Enc,
)
require.NoError(t, err)
key, access, err = access.Restrict(key, restriction)
scope.APIKey, scope.EncryptionAccess, err =
scope.EncryptionAccess.Restrict(scope.APIKey,
libuplink.EncryptionRestriction{Bucket: "test0"},
libuplink.EncryptionRestriction{Bucket: "test1"})
require.NoError(t, err)
caveat := macaroon.Caveat{}
caveat.DisallowReads = true
caveat.AllowedPaths = append(caveat.AllowedPaths,
&macaroon.Caveat_Path{
Bucket: []byte("test1"),
},
)
key, err = key.Restrict(caveat)
require.NoError(t, err)
p, err = ul.OpenProject(ctx, satelliteAddr, key)
p, err = ul.OpenProject(ctx, scope.SatelliteAddr, scope.APIKey)
require.NoError(t, err)
defer ctx.Check(p.Close)

View File

@ -154,6 +154,7 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
}
flags.Var(fieldvalue, flagname, help)
markHidden := false
if onlyForSetup {
setBoolAnnotation(flags, flagname, "setup")
}
@ -161,6 +162,14 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
setBoolAnnotation(flags, flagname, "user")
}
if field.Tag.Get("hidden") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "hidden")
}
if field.Tag.Get("deprecated") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "deprecated")
}
if markHidden {
err := flags.MarkHidden(flagname)
if err != nil {
panic(fmt.Sprintf("mark hidden failed %s: %v", flagname, err))
@ -239,9 +248,21 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
if field.Tag.Get("user") == "true" {
setBoolAnnotation(flags, flagname, "user")
}
markHidden := false
if field.Tag.Get("hidden") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "hidden")
}
if field.Tag.Get("deprecated") == "true" {
markHidden = true
setBoolAnnotation(flags, flagname, "deprecated")
}
if markHidden {
err := flags.MarkHidden(flagname)
check(err)
if err != nil {
panic(fmt.Sprintf("mark hidden failed %s: %v", flagname, err))
}
}
}
}

View File

@ -7,16 +7,13 @@ import (
"context"
"errors"
"flag"
"io/ioutil"
"os"
"testing"
"time"
"github.com/minio/cli"
minio "github.com/minio/minio/cmd"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
@ -24,11 +21,8 @@ import (
"storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testidentity"
"storj.io/storj/internal/testplanet"
"storj.io/storj/internal/testrand"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/cfgstruct"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/macaroon"
"storj.io/storj/pkg/miniogw"
"storj.io/storj/pkg/storj"
"storj.io/storj/satellite/console"
@ -46,62 +40,22 @@ func TestUploadDownload(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
planet, err := testplanet.New(t, 1, 30, 0)
planet, err := testplanet.New(t, 1, 30, 1)
assert.NoError(t, err)
defer ctx.Check(planet.Shutdown)
// add project to satisfy constraint
project, err := planet.Satellites[0].DB.Console().Projects().Insert(context.Background(), &console.Project{
_, err = planet.Satellites[0].DB.Console().Projects().Insert(context.Background(), &console.Project{
Name: "testProject",
})
assert.NoError(t, err)
apiKey, err := macaroon.NewAPIKey([]byte("testSecret"))
assert.NoError(t, err)
apiKeyInfo := console.APIKeyInfo{
ProjectID: project.ID,
Name: "testKey",
Secret: []byte("testSecret"),
}
// add api key to db
_, err = planet.Satellites[0].DB.Console().APIKeys().Create(context.Background(), apiKey.Head(), apiKeyInfo)
assert.NoError(t, err)
// bind default values to config
var gwCfg config
cfgstruct.Bind(&pflag.FlagSet{}, &gwCfg, cfgstruct.UseDevDefaults())
var uplinkCfg uplink.Config
cfgstruct.Bind(&pflag.FlagSet{}, &uplinkCfg, cfgstruct.UseDevDefaults())
// minio config directory
gwCfg.Minio.Dir = ctx.Dir("minio")
// addresses
gwCfg.Server.Address = "127.0.0.1:7777"
uplinkCfg.Client.SatelliteAddr = planet.Satellites[0].Addr()
// keys
uplinkCfg.Client.APIKey = "apiKey"
// Encryption key
passphrase := testrand.BytesInt(testrand.Intn(100) + 1)
encryptionKey, err := storj.NewKey(passphrase)
require.NoError(t, err)
filename := ctx.File("encryption.key")
err = ioutil.WriteFile(filename, encryptionKey[:], os.FileMode(0400))
require.NoError(t, err)
uplinkCfg.Enc.KeyFilepath = filename
// redundancy
uplinkCfg.RS.MinThreshold = 7
uplinkCfg.RS.RepairThreshold = 8
uplinkCfg.RS.SuccessThreshold = 9
uplinkCfg.RS.MaxThreshold = 10
uplinkCfg := planet.Uplinks[0].GetConfig(planet.Satellites[0])
planet.Start(ctx)
@ -127,8 +81,8 @@ func TestUploadDownload(t *testing.T) {
Satellite: planet.Satellites[0].Addr(),
AccessKey: gwCfg.Minio.AccessKey,
SecretKey: gwCfg.Minio.SecretKey,
APIKey: uplinkCfg.Client.APIKey,
EncryptionKey: string(encryptionKey[:]),
APIKey: uplinkCfg.Legacy.Client.APIKey,
EncryptionKey: "fake-encryption-key",
NoSSL: true,
})
assert.NoError(t, err)
@ -197,12 +151,12 @@ func runGateway(ctx context.Context, gwCfg config, uplinkCfg uplink.Config, log
return err
}
apiKey, err := libuplink.ParseAPIKey(uplinkCfg.Client.APIKey)
apiKey, err := libuplink.ParseAPIKey(uplinkCfg.Legacy.Client.APIKey)
if err != nil {
return err
}
project, err := uplink.OpenProject(ctx, uplinkCfg.Client.SatelliteAddr, apiKey)
project, err := uplink.OpenProject(ctx, uplinkCfg.Legacy.Client.SatelliteAddr, apiKey)
if err != nil {
return err
}

223
pkg/process/config.go Normal file
View File

@ -0,0 +1,223 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package process
import (
"bytes"
"flag"
"io/ioutil"
"os"
"path/filepath"
"sort"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/zeebo/errs"
yaml "gopkg.in/yaml.v2"
)
// SaveConfigOption is a function that updates the options for SaveConfig.
type SaveConfigOption func(*SaveConfigOptions)
// SaveConfigOptions controls the behavior of SaveConfig.
type SaveConfigOptions struct {
Overrides map[string]interface{}
RemoveDeprecated bool
}
// SaveConfigWithOverrides sets the overrides to the provided map.
func SaveConfigWithOverrides(overrides map[string]interface{}) SaveConfigOption {
return func(opts *SaveConfigOptions) {
opts.Overrides = overrides
}
}
// SaveConfigWithOverride adds a single override to SaveConfig.
func SaveConfigWithOverride(name string, value interface{}) SaveConfigOption {
return func(opts *SaveConfigOptions) {
if opts.Overrides == nil {
opts.Overrides = make(map[string]interface{})
}
opts.Overrides[name] = value
}
}
// SaveConfigRemovingDeprecated tells SaveConfig to not store deprecated flags.
func SaveConfigRemovingDeprecated() SaveConfigOption {
return func(opts *SaveConfigOptions) {
opts.RemoveDeprecated = true
}
}
// SaveConfig will save only the user-specific flags with default values to
// outfile with specific values specified in 'overrides' overridden.
func SaveConfig(cmd *cobra.Command, outfile string, opts ...SaveConfigOption) error {
// step 0. apply any options to change the behavior
//
var options SaveConfigOptions
for _, opt := range opts {
opt(&options)
}
// step 1. load all of the configuration settings we are going to save
//
flags := cmd.Flags()
vip, err := Viper(cmd)
if err != nil {
return errs.Wrap(err)
}
if err := vip.MergeConfigMap(options.Overrides); err != nil {
return errs.Wrap(err)
}
settings := vip.AllSettings()
// step 2. construct some data describing what exactly we're saving to the
// config file, and how they're saved.
//
type configValue struct {
value interface{}
comment string
set bool
}
flat := make(map[string]configValue)
flatKeys := make([]string, 0)
// N.B. we have to pre-declare the function so that it can make recursive calls.
var filterAndFlatten func(string, map[string]interface{})
filterAndFlatten = func(base string, settings map[string]interface{}) {
for key, value := range settings {
if value, ok := value.(map[string]interface{}); ok {
filterAndFlatten(base+key+".", value)
continue
}
fullKey := base + key
// since this key can't affect anything from the config file and must be present
// on the command line, remove it so as to not mislead anyone
if fullKey == "defaults" {
continue
}
// gather information about the flag under consideration
var (
changed bool
setup bool
hidden bool
user bool
deprecated bool
comment string
typ string
_, overrideExists = options.Overrides[fullKey]
)
if f := flags.Lookup(fullKey); f != nil { // first check pflags
changed = f.Changed
setup = readBoolAnnotation(f, "setup")
hidden = readBoolAnnotation(f, "hidden")
user = readBoolAnnotation(f, "user")
deprecated = readBoolAnnotation(f, "deprecated")
comment = f.Usage
typ = f.Value.Type()
} else if f := flag.Lookup(fullKey); f != nil { // then stdlib flags
changed = f.Value.String() != f.DefValue
comment = f.Usage
} else {
// by default we store config values we know nothing about. we
// absue the meaning of "changed" to include this case.
changed = true
}
// in any of these cases, don't store the key in the config file
if setup || hidden || options.RemoveDeprecated && deprecated {
continue
}
// viper is super cool and doesn't cast floats automatically, so we
// handle that ourselves.
if typ == "float64" {
value = cast.ToFloat64(value)
}
flatKeys = append(flatKeys, fullKey)
flat[fullKey] = configValue{
value: value,
comment: comment,
set: user || changed || overrideExists,
}
}
}
filterAndFlatten("", settings)
sort.Strings(flatKeys)
// step 3. write out the configuration file
//
var nl = []byte("\n")
var lines [][]byte
for _, key := range flatKeys {
config := flat[key]
if config.comment != "" {
lines = append(lines, []byte("# "+config.comment))
}
data, err := yaml.Marshal(map[string]interface{}{key: config.value})
if err != nil {
return errs.Wrap(err)
}
dataLines := bytes.Split(bytes.TrimSpace(data), nl)
// if the config value is set, concat in the yaml lines
if config.set {
lines = append(lines, dataLines...)
} else {
// otherwise, add them in but commented out
for _, line := range dataLines {
lines = append(lines, append([]byte("# "), line...))
}
}
// add a blank line separator
lines = append(lines, nil)
}
return errs.Wrap(atomicWrite(outfile, 0600, bytes.Join(lines, nl)))
}
// readBoolAnnotation is a helper to see if a boolean annotation is set to true on the flag.
func readBoolAnnotation(flag *pflag.Flag, key string) bool {
annotation := flag.Annotations[key]
return len(annotation) > 0 && annotation[0] == "true"
}
// atomicWrite is a helper to atomically write the data to the outfile.
func atomicWrite(outfile string, mode os.FileMode, data []byte) (err error) {
fh, err := ioutil.TempFile(filepath.Dir(outfile), filepath.Base(outfile))
if err != nil {
return errs.Wrap(err)
}
defer func() {
if err != nil {
err = errs.Combine(err, fh.Close())
err = errs.Combine(err, os.Remove(fh.Name()))
}
}()
if _, err := fh.Write(data); err != nil {
return errs.Wrap(err)
}
if err := fh.Sync(); err != nil {
return errs.Wrap(err)
}
if err := fh.Close(); err != nil {
return errs.Wrap(err)
}
if err := os.Rename(fh.Name(), outfile); err != nil {
return errs.Wrap(err)
}
return nil
}

View File

@ -7,12 +7,10 @@ import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"sort"
"strings"
"sync"
"syscall"
@ -38,19 +36,18 @@ const DefaultCfgFilename = "config.yaml"
var (
mon = monkit.Package()
contextMtx sync.Mutex
commandMtx sync.Mutex
contexts = map[*cobra.Command]context.Context{}
configMtx sync.Mutex
configs = map[*cobra.Command][]interface{}{}
configs = map[*cobra.Command][]interface{}{}
vipers = map[*cobra.Command]*viper.Viper{}
)
// Bind sets flags on a command that match the configuration struct
// 'config'. It ensures that the config has all of the values loaded into it
// when the command runs.
func Bind(cmd *cobra.Command, config interface{}, opts ...cfgstruct.BindOpt) {
configMtx.Lock()
defer configMtx.Unlock()
commandMtx.Lock()
defer commandMtx.Unlock()
cfgstruct.Bind(cmd.Flags(), config, opts...)
configs[cmd] = append(configs[cmd], config)
@ -75,91 +72,11 @@ func Exec(cmd *cobra.Command) {
_ = cmd.Execute()
}
// SaveConfig will save only the user-specific flags with default values to
// outfile with specific values specified in 'overrides' overridden.
func SaveConfig(flagset *pflag.FlagSet, outfile string, overrides map[string]interface{}) error {
return saveConfig(flagset, outfile, overrides, false)
}
// SaveConfigWithAllDefaults will save all flags with default values to outfile
// with specific values specified in 'overrides' overridden.
func SaveConfigWithAllDefaults(flagset *pflag.FlagSet, outfile string, overrides map[string]interface{}) error {
return saveConfig(flagset, outfile, overrides, true)
}
func saveConfig(flagset *pflag.FlagSet, outfile string, overrides map[string]interface{}, saveAllDefaults bool) error {
// we previously used Viper here, but switched to a custom serializer to allow comments
//todo: switch back to Viper once go-yaml v3 is released and its supports writing comments?
flagset.AddFlagSet(pflag.CommandLine)
//sort keys
var keys []string
flagset.VisitAll(func(f *pflag.Flag) { keys = append(keys, f.Name) })
sort.Strings(keys)
//serialize
var sb strings.Builder
w := &sb
for _, k := range keys {
f := flagset.Lookup(k)
if readBoolAnnotation(f, "setup") {
continue
}
if f.Hidden == true {
continue
}
var overriddenValue interface{}
var overrideExist bool
if overrides != nil {
overriddenValue, overrideExist = overrides[k]
}
if !saveAllDefaults && !readBoolAnnotation(f, "user") && !f.Changed && !overrideExist {
continue
}
value := f.Value.String()
if overriddenValue != nil {
value = fmt.Sprintf("%v", overriddenValue)
}
//print usage info
if f.Usage != "" {
fmt.Fprintf(w, "# %s\n", f.Usage)
}
//print commented key (beginning of value assignement line)
if readBoolAnnotation(f, "user") || f.Changed || overrideExist {
fmt.Fprintf(w, "%s: ", k)
} else {
fmt.Fprintf(w, "# %s: ", k)
}
//print value (remainder of value assignement line)
switch f.Value.Type() {
case "string":
// save ourselves 250+ lines of code and just double quote strings
fmt.Fprintf(w, "%q\n\n", value)
default:
//assume that everything else doesn't have fancy control characters
fmt.Fprintf(w, "%s\n\n", value)
}
}
err := ioutil.WriteFile(outfile, []byte(sb.String()), os.FileMode(0644))
if err != nil {
return err
}
fmt.Println("Your configuration is saved to:", outfile)
return nil
}
func readBoolAnnotation(flag *pflag.Flag, key string) bool {
annotation := flag.Annotations[key]
return len(annotation) > 0 && annotation[0] == "true"
}
// Ctx returns the appropriate context.Context for ExecuteWithConfig commands
func Ctx(cmd *cobra.Command) context.Context {
contextMtx.Lock()
defer contextMtx.Unlock()
commandMtx.Lock()
defer commandMtx.Unlock()
ctx := contexts[cmd]
if ctx == nil {
ctx = context.Background()
@ -176,6 +93,38 @@ func Ctx(cmd *cobra.Command) context.Context {
return ctx
}
// Viper returns the appropriate *viper.Viper for the command, creating if necessary.
func Viper(cmd *cobra.Command) (*viper.Viper, error) {
commandMtx.Lock()
defer commandMtx.Unlock()
if vip := vipers[cmd]; vip != nil {
return vip, nil
}
vip := viper.New()
if err := vip.BindPFlags(cmd.Flags()); err != nil {
return nil, err
}
vip.SetEnvPrefix("storj")
vip.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
vip.AutomaticEnv()
cfgFlag := cmd.Flags().Lookup("config-dir")
if cfgFlag != nil && cfgFlag.Value.String() != "" {
path := filepath.Join(os.ExpandEnv(cfgFlag.Value.String()), DefaultCfgFilename)
if cmd.Annotations["type"] != "setup" || fileExists(path) {
vip.SetConfigFile(path)
if err := vip.ReadInConfig(); err != nil {
return nil, err
}
}
}
vipers[cmd] = vip
return vip, nil
}
var traceOut = flag.String("debug.trace-out", "", "If set, a path to write a process trace SVG to")
func cleanup(cmd *cobra.Command) {
@ -194,45 +143,30 @@ func cleanup(cmd *cobra.Command) {
ctx := context.Background()
defer mon.TaskNamed("root")(&ctx)(&err)
vip := viper.New()
err = vip.BindPFlags(cmd.Flags())
vip, err := Viper(cmd)
if err != nil {
return err
}
vip.SetEnvPrefix("storj")
vip.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
vip.AutomaticEnv()
cfgFlag := cmd.Flags().Lookup("config-dir")
if cfgFlag != nil && cfgFlag.Value.String() != "" {
path := filepath.Join(os.ExpandEnv(cfgFlag.Value.String()), DefaultCfgFilename)
if cmd.Annotations["type"] != "setup" || fileExists(path) {
vip.SetConfigFile(path)
err = vip.ReadInConfig()
if err != nil {
return err
}
}
}
configMtx.Lock()
commandMtx.Lock()
configValues := configs[cmd]
configMtx.Unlock()
commandMtx.Unlock()
var (
brokenKeys = map[string]struct{}{}
missingKeys = map[string]struct{}{}
usedKeys = map[string]struct{}{}
allKeys = map[string]struct{}{}
allSettings = vip.AllSettings()
)
// Hacky hack: these two keys are noprefix which breaks all scoping
if val, ok := allSettings["api-key"]; ok {
allSettings["client.api-key"] = val
allSettings["legacy.client.api-key"] = val
delete(allSettings, "api-key")
}
if val, ok := allSettings["satellite-addr"]; ok {
allSettings["client.satellite-addr"] = val
allSettings["legacy.client.satellite-addr"] = val
delete(allSettings, "satellite-addr")
}
@ -241,36 +175,44 @@ func cleanup(cmd *cobra.Command) {
res := structs.Decode(allSettings, config)
for key := range res.Used {
usedKeys[key] = struct{}{}
allKeys[key] = struct{}{}
}
for key := range res.Missing {
missingKeys[key] = struct{}{}
allKeys[key] = struct{}{}
}
for key := range res.Broken {
brokenKeys[key] = struct{}{}
allKeys[key] = struct{}{}
}
}
for key := range missingKeys {
for key := range allKeys {
// Check if the key is a flag, and if so, propagate it.
if f := cmd.Flags().Lookup(key); f != nil {
val := vip.GetString(key)
err := f.Value.Set(val)
f.Changed = val != f.DefValue
if err != nil {
brokenKeys[key] = struct{}{}
} else {
usedKeys[key] = struct{}{}
}
} else if f := flag.Lookup(key); f != nil {
err := f.Value.Set(vip.GetString(key))
if err != nil {
brokenKeys[key] = struct{}{}
} else {
usedKeys[key] = struct{}{}
}
}
// A key is only missing if it was missing from every single config struct, so
// remove all of the used keys from it.
if _, ok := usedKeys[key]; ok {
delete(missingKeys, key)
continue
}
// Attempt to set through the flags any keys that were missing from all of the
// config structs.
flag := cmd.Flags().Lookup(key)
if flag == nil {
continue
}
changed := flag.Changed
if err := flag.Value.Set(vip.GetString(key)); err != nil {
brokenKeys[key] = struct{}{}
}
flag.Changed = changed
delete(missingKeys, key)
}
logger, err := newLogger()
@ -309,13 +251,13 @@ func cleanup(cmd *cobra.Command) {
var workErr error
work := func(ctx context.Context) {
contextMtx.Lock()
commandMtx.Lock()
contexts[cmd] = ctx
contextMtx.Unlock()
commandMtx.Unlock()
defer func() {
contextMtx.Lock()
commandMtx.Lock()
delete(contexts, cmd)
contextMtx.Unlock()
commandMtx.Unlock()
}()
workErr = internalRun(cmd, args)

View File

@ -21,6 +21,8 @@ func setenv(key, value string) func() {
return func() { _ = os.Setenv(key, old) }
}
var testZ = flag.Int("z", 0, "z flag (stdlib)")
func TestExec_PropagatesSettings(t *testing.T) {
// Set up a command that does nothing.
cmd := &cobra.Command{RunE: func(cmd *cobra.Command, args []string) error { return nil }}
@ -31,7 +33,6 @@ func TestExec_PropagatesSettings(t *testing.T) {
}
Bind(cmd, &config)
y := cmd.Flags().Int("y", 0, "y flag (command)")
z := flag.Int("z", 0, "z flag (stdlib)")
// Set some environment variables for viper.
defer setenv("STORJ_X", "1")()
@ -44,14 +45,14 @@ func TestExec_PropagatesSettings(t *testing.T) {
// Check that the variables are now bound.
require.Equal(t, 1, config.X)
require.Equal(t, 2, *y)
require.Equal(t, 3, *z)
require.Equal(t, 3, *testZ)
}
func TestHidden(t *testing.T) {
// Set up a command that does nothing.
cmd := &cobra.Command{RunE: func(cmd *cobra.Command, args []string) error { return nil }}
// Define a config struct and some flags.
// Define a config struct with a hidden field.
var config struct {
W int `default:"0" hidden:"false"`
X int `default:"0" hidden:"true"`
@ -64,10 +65,12 @@ func TestHidden(t *testing.T) {
ctx := testcontext.New(t)
testConfigFile := ctx.File("testconfig.yaml")
defer ctx.Cleanup()
overrides := map[string]interface{}{}
// Test that only the configs that are not hidden show up in config file
err := SaveConfigWithAllDefaults(cmd.Flags(), testConfigFile, overrides)
// Run the command through the exec call.
Exec(cmd)
// Ensure that the file saves only the necessary data.
err := SaveConfig(cmd, testConfigFile)
require.NoError(t, err)
actualConfigFile, err := ioutil.ReadFile(testConfigFile)

View File

@ -23,7 +23,7 @@
# checker.reliability-cache-staleness: 5m0s
# server address of the graphql api gateway and frontend app
# console.address: ":10100"
# console.address: :10100
# auth token needed for access to registration token creation endpoint
# console.auth-token: ""
@ -41,7 +41,7 @@
# console.stripe-key: ""
# satellite database connection string
# database: "postgres://"
# database: postgres://
# Maximum Database Connection Lifetime, -1ns means the stdlib default
# db.conn_max_lifetime: -1ns
@ -53,14 +53,11 @@
# db.max_open_conns: 25
# address to listen on for debug endpoints
# debug.addr: "127.0.0.1:0"
# debug.addr: 127.0.0.1:0
# If set, a path to write a process trace SVG to
# debug.trace-out: ""
# determines which set of configuration defaults to use. can either be 'dev' or 'release'
defaults: "release"
# the interval at which the satellite attempts to find new nodes via random node ID lookups
# discovery.discovery-interval: 1s
@ -92,16 +89,16 @@ defaults: "release"
# help: false
# path to the certificate chain for this identity
identity.cert-path: "/root/.local/share/storj/identity/satellite/identity.cert"
identity.cert-path: /root/.local/share/storj/identity/satellite/identity.cert
# path to the private key for this identity
identity.key-path: "/root/.local/share/storj/identity/satellite/identity.key"
identity.key-path: /root/.local/share/storj/identity/satellite/identity.key
# alpha is a system wide concurrency parameter
# kademlia.alpha: 5
# the Kademlia node to bootstrap against
# kademlia.bootstrap-addr: "bootstrap.storj.io:8888"
# kademlia.bootstrap-addr: bootstrap.storj.io:8888
# the base interval to wait when retrying bootstrap
# kademlia.bootstrap-backoff-base: 1s
@ -113,7 +110,7 @@ identity.key-path: "/root/.local/share/storj/identity/satellite/identity.key"
# kademlia.bucket-size: 20
# the path for storage node db services to be created on
# kademlia.db-path: "testdata/kademlia"
# kademlia.db-path: testdata/kademlia
# the public address of the Kademlia node, useful for nodes behind NAT
kademlia.external-address: ""
@ -128,7 +125,7 @@ kademlia.operator.wallet: ""
# kademlia.replacement-cache-size: 5
# what to use for storing real-time accounting data
# live-accounting.storage-backend: "plainmemory"
# live-accounting.storage-backend: plainmemory
# if true, log function filename and line number
# log.caller: false
@ -137,19 +134,19 @@ kademlia.operator.wallet: ""
# log.development: false
# configures log encoding. can either be 'console' or 'json'
# log.encoding: "console"
# log.encoding: console
# the minimum log level to log
# log.level: info
# can be stdout, stderr, or a filename
# log.output: "stderr"
# log.output: stderr
# if true, log stack traces
# log.stack: false
# smtp authentication type
# mail.auth-type: "login"
# mail.auth-type: login
# oauth2 app's client id
# mail.client-id: ""
@ -179,13 +176,13 @@ kademlia.operator.wallet: ""
# mail.token-uri: ""
# server address of the marketing Admin GUI
# marketing.address: "127.0.0.1:8090"
# marketing.address: 127.0.0.1:8090
# path to static resources
# marketing.static-dir: ""
# the database connection string to use
# metainfo.database-url: "postgres://"
# metainfo.database-url: postgres://
# how long to wait for new observers before starting iteration
# metainfo.loop.coalesce-duration: 5s
@ -224,13 +221,13 @@ kademlia.operator.wallet: ""
# metainfo.rs.validate: true
# address to send telemetry to
# metrics.addr: "collectora.storj.io:9000"
# metrics.addr: collectora.storj.io:9000
# application name for telemetry identification
# metrics.app: "satellite"
# metrics.app: satellite
# application suffix
# metrics.app-suffix: "-release"
# metrics.app-suffix: -release
# instance id prefix
# metrics.instance-prefix: ""
@ -239,7 +236,7 @@ kademlia.operator.wallet: ""
# metrics.interval: 1m0s
# path to log for oom notices
# monkit.hw.oomlog: "/var/log/kern.log"
# monkit.hw.oomlog: /var/log/kern.log
# how long until an order expires
# orders.expiration: 168h0m0s
@ -332,7 +329,7 @@ kademlia.operator.wallet: ""
# rollup.max-alpha-usage: 25.0 GB
# public address to listen on
server.address: ":7777"
server.address: :7777
# log all GRPC traffic to zap logger
server.debug-log-traffic: false
@ -347,13 +344,13 @@ server.debug-log-traffic: false
# server.peer-ca-whitelist-path: ""
# identity version(s) the server will be allowed to talk to
# server.peer-id-versions: "latest"
# server.peer-id-versions: latest
# private address to listen on
server.private-address: "127.0.0.1:7778"
server.private-address: 127.0.0.1:7778
# url for revocation database (e.g. bolt://some.db OR redis://127.0.0.1:6378?db=2&password=abc123)
# server.revocation-dburl: "bolt://testdata/revocations.db"
# server.revocation-dburl: bolt://testdata/revocations.db
# if true, uses peer ca whitelist checking
# server.use-peer-ca-whitelist: true
@ -368,8 +365,7 @@ server.private-address: "127.0.0.1:7778"
# version.request-timeout: 1m0s
# server address to check its version against
# version.server-address: "https://version.alpha.storj.io"
# version.server-address: https://version.alpha.storj.io
# length of time before a voucher expires
# vouchers.expiration: 720h0m0s

View File

@ -4,13 +4,21 @@
package uplink
import (
"io/ioutil"
"strings"
"time"
"github.com/zeebo/errs"
"gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/internal/memory"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/peertls/tlsopts"
"storj.io/storj/pkg/storj"
)
var mon = monkit.Package()
// RSConfig is a configuration struct that keeps details about default
// redundancy strategy information
type RSConfig struct {
@ -25,18 +33,13 @@ type RSConfig struct {
// EncryptionConfig is a configuration struct that keeps details about
// encrypting segments
type EncryptionConfig struct {
EncryptionKey string `help:"the root key for encrypting the data which will be stored in KeyFilePath" setup:"true"`
KeyFilepath string `help:"the path to the file which contains the root key for encrypting the data"`
EncAccessFilepath string `help:"the path to a file containing a serialized encryption access"`
DataType int `help:"Type of encryption to use for content and metadata (2=AES-GCM, 3=SecretBox)" default:"2"`
PathType int `help:"Type of encryption to use for paths (1=Unencrypted, 2=AES-GCM, 3=SecretBox)" default:"2"`
DataType int `help:"Type of encryption to use for content and metadata (2=AES-GCM, 3=SecretBox)" default:"2"`
PathType int `help:"Type of encryption to use for paths (1=Unencrypted, 2=AES-GCM, 3=SecretBox)" default:"2"`
}
// ClientConfig is a configuration struct for the uplink that controls how
// to talk to the rest of the network.
type ClientConfig struct {
APIKey string `default:"" help:"the api key to use for the satellite" noprefix:"true"`
SatelliteAddr string `releaseDefault:"127.0.0.1:7777" devDefault:"127.0.0.1:10000" help:"the address to use for the satellite" noprefix:"true"`
MaxInlineSize memory.Size `help:"max inline segment size in bytes" default:"4KiB"`
SegmentSize memory.Size `help:"the size of a segment in bytes" default:"64MiB"`
RequestTimeout time.Duration `help:"timeout for request" default:"0h2m00s"`
@ -45,12 +48,91 @@ type ClientConfig struct {
// Config uplink configuration
type Config struct {
ScopeConfig
Client ClientConfig
RS RSConfig
Enc EncryptionConfig
TLS tlsopts.Config
}
// ScopeConfig holds information about which scopes exist and are selected.
type ScopeConfig struct {
Scopes map[string]string `internal:"true"`
Scope string `help:"the serialized scope, or name of the scope to use" default:""`
Legacy // Holds on to legacy configuration values
}
// Legacy holds deprecated configuration values
type Legacy struct {
Client struct {
APIKey string `default:"" help:"the api key to use for the satellite (deprecated)" noprefix:"true" deprecated:"true"`
SatelliteAddr string `releaseDefault:"127.0.0.1:7777" devDefault:"127.0.0.1:10000" help:"the address to use for the satellite (deprecated)" noprefix:"true"`
}
Enc struct {
EncryptionKey string `help:"the root key for encrypting the data which will be stored in KeyFilePath (deprecated)" setup:"true" deprecated:"true"`
KeyFilepath string `help:"the path to the file which contains the root key for encrypting the data (deprecated)" deprecated:"true"`
EncAccessFilepath string `help:"the path to a file containing a serialized encryption access (deprecated)" deprecated:"true"`
}
}
// GetScope returns the appropriate scope for the config.
func (c ScopeConfig) GetScope() (_ *libuplink.Scope, err error) {
defer mon.Task()(nil)(&err)
// if a scope exists for that name, try to load it.
if data, ok := c.Scopes[c.Scope]; ok && c.Scope != "" {
return libuplink.ParseScope(data)
}
// Otherwise, try to load the scope name as a serialized scope.
if scope, err := libuplink.ParseScope(c.Scope); err == nil {
return scope, nil
}
// fall back to trying to load the legacy values.
apiKey, err := libuplink.ParseAPIKey(c.Legacy.Client.APIKey)
if err != nil {
return nil, err
}
satelliteAddr := c.Legacy.Client.SatelliteAddr
if satelliteAddr == "" {
return nil, errs.New("must specify a satellite address")
}
var encAccess *libuplink.EncryptionAccess
if c.Legacy.Enc.EncAccessFilepath != "" {
data, err := ioutil.ReadFile(c.Legacy.Enc.EncAccessFilepath)
if err != nil {
return nil, errs.Wrap(err)
}
encAccess, err = libuplink.ParseEncryptionAccess(strings.TrimSpace(string(data)))
if err != nil {
return nil, err
}
} else {
data := []byte(c.Legacy.Enc.EncryptionKey)
if c.Legacy.Enc.KeyFilepath != "" {
data, err = ioutil.ReadFile(c.Legacy.Enc.KeyFilepath)
if err != nil {
return nil, errs.Wrap(err)
}
}
key, err := storj.NewKey(data)
if err != nil {
return nil, errs.Wrap(err)
}
encAccess = libuplink.NewEncryptionAccessWithDefaultKey(*key)
}
return &libuplink.Scope{
APIKey: apiKey,
SatelliteAddr: satelliteAddr,
EncryptionAccess: encAccess,
}, nil
}
// GetRedundancyScheme returns the configured redundancy scheme for new uploads
func (c Config) GetRedundancyScheme() storj.RedundancyScheme {
return storj.RedundancyScheme{

View File

@ -1,41 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package setup
import (
"os"
"github.com/zeebo/errs"
)
// SaveEncryptionKey generates a Storj key from the inputKey and save it into a
// new file created in filepath.
func SaveEncryptionKey(inputKey string, filepath string) error {
switch {
case len(inputKey) == 0:
return Error.New("inputKey is empty")
case filepath == "":
return Error.New("filepath is empty")
}
file, err := os.OpenFile(filepath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
if os.IsNotExist(err) {
return Error.New("directory path doesn't exist. %+v", err)
}
if os.IsExist(err) {
return Error.New("file key already exists. %+v", err)
}
return Error.Wrap(err)
}
defer func() {
err = Error.Wrap(errs.Combine(err, file.Close()))
}()
_, err = file.Write([]byte(inputKey))
return err
}

View File

@ -1,82 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package setup_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testrand"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink/setup"
)
func TestSaveEncryptionKey(t *testing.T) {
generateInputKey := func() string {
return string(testrand.BytesInt(testrand.Intn(storj.KeySize*3) + 1))
}
t.Run("ok", func(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
inputKey := generateInputKey()
filename := ctx.File("storj-test-cmd-uplink", "encryption.key")
err := setup.SaveEncryptionKey(inputKey, filename)
require.NoError(t, err)
savedKey, err := ioutil.ReadFile(filename)
require.NoError(t, err)
assert.Equal(t, inputKey, string(savedKey))
})
t.Run("error: empty input key", func(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
filename := ctx.File("storj-test-cmd-uplink", "encryption.key")
err := setup.SaveEncryptionKey("", filename)
require.Error(t, err)
})
t.Run("error: empty filepath", func(t *testing.T) {
inputKey := generateInputKey()
err := setup.SaveEncryptionKey(inputKey, "")
require.Error(t, err)
})
t.Run("error: unexisting dir", func(t *testing.T) {
// Create a directory and remove it for making sure that the path doesn't
// exist
ctx := testcontext.New(t)
dir := ctx.Dir("storj-test-cmd-uplink")
ctx.Cleanup()
inputKey := generateInputKey()
filename := filepath.Join(dir, "enc.key")
err := setup.SaveEncryptionKey(inputKey, filename)
require.Errorf(t, err, "directory path doesn't exist")
})
t.Run("error: file already exists", func(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
inputKey := generateInputKey()
filename := ctx.File("encryption.key")
require.NoError(t, ioutil.WriteFile(filename, nil, os.FileMode(0600)))
err := setup.SaveEncryptionKey(inputKey, filename)
require.Errorf(t, err, "file key already exists")
})
}

View File

@ -1,51 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package setup
import (
"context"
"io/ioutil"
"strings"
"github.com/zeebo/errs"
"gopkg.in/spacemonkeygo/monkit.v2"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink"
)
var (
mon = monkit.Package()
// Error is the class of errors returned by this package
Error = errs.Class("uplink setup")
)
// LoadEncryptionAccess loads an EncryptionAccess from the values specified in the encryption config.
func LoadEncryptionAccess(ctx context.Context, cfg uplink.EncryptionConfig) (_ *libuplink.EncryptionAccess, err error) {
defer mon.Task()(&ctx)(&err)
if cfg.EncAccessFilepath != "" {
data, err := ioutil.ReadFile(cfg.EncAccessFilepath)
if err != nil {
return nil, errs.Wrap(err)
}
return libuplink.ParseEncryptionAccess(strings.TrimSpace(string(data)))
}
data := []byte(cfg.EncryptionKey)
if cfg.KeyFilepath != "" {
data, err = ioutil.ReadFile(cfg.KeyFilepath)
if err != nil {
return nil, errs.Wrap(err)
}
}
key, err := storj.NewKey(data)
if err != nil {
return nil, errs.Wrap(err)
}
return libuplink.NewEncryptionAccessWithDefaultKey(*key), nil
}

View File

@ -1,71 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package setup_test
import (
"context"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/require"
"storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testrand"
libuplink "storj.io/storj/lib/uplink"
"storj.io/storj/pkg/storj"
"storj.io/storj/uplink"
"storj.io/storj/uplink/setup"
)
func TestLoadEncryptionAccess(t *testing.T) {
saveRawCtx := func(access *libuplink.EncryptionAccess) (filepath string, clenaup func()) {
t.Helper()
ctx := testcontext.New(t)
filename := ctx.File("encryption.ctx")
data, err := access.Serialize()
require.NoError(t, err)
err = ioutil.WriteFile(filename, []byte(data), os.FileMode(0400))
require.NoError(t, err)
return filename, ctx.Cleanup
}
t.Run("ok: reading from file", func(t *testing.T) {
passphrase := testrand.BytesInt(1 + testrand.Intn(100))
key, err := storj.NewKey(passphrase)
require.NoError(t, err)
access := libuplink.NewEncryptionAccessWithDefaultKey(*key)
filename, cleanup := saveRawCtx(access)
defer cleanup()
gotCtx, err := setup.LoadEncryptionAccess(context.Background(), uplink.EncryptionConfig{
EncAccessFilepath: filename,
})
require.NoError(t, err)
require.Equal(t, access, gotCtx)
})
t.Run("ok: empty filepath", func(t *testing.T) {
gotCtx, err := setup.LoadEncryptionAccess(context.Background(), uplink.EncryptionConfig{
EncAccessFilepath: "",
})
require.NoError(t, err)
require.NotNil(t, gotCtx)
})
t.Run("error: file not found", func(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
filename := ctx.File("encryption.ctx")
_, err := setup.LoadEncryptionAccess(context.Background(), uplink.EncryptionConfig{
EncAccessFilepath: filename,
})
require.Error(t, err)
})
}