package main import ( "errors" "fmt" "isle/daemon/daecommon" "os" "path/filepath" "syscall" ) // minio-client keeps a configuration directory which contains various pieces of // information which may or may not be useful. Unfortunately when it initializes // this directory it likes to print some annoying logs, so we pre-initialize in // order to prevent it from doing so. func initMCConfigDir(envVars daecommon.EnvVars) (string, error) { var ( path = filepath.Join(envVars.StateDir.Path, "mc") sharePath = filepath.Join(path, "share") configJSONPath = filepath.Join(path, "config.json") ) if err := os.MkdirAll(sharePath, 0700); err != nil { return "", fmt.Errorf("creating %q: %w", sharePath, err) } if err := os.WriteFile(configJSONPath, []byte(`{}`), 0600); err != nil { return "", fmt.Errorf("writing %q: %w", configJSONPath, err) } return path, nil } var subCmdGarageMC = subCmd{ name: "mc", descr: "Runs the mc (minio-client) binary. The isle garage can be accessed under the `garage` alias", do: func(ctx subCmdCtx) error { keyID := ctx.flags.StringP( "key-id", "i", "", "Optional key ID to use, defaults to that of the shared global key", ) keySecret := ctx.flags.StringP( "key-secret", "s", "", "Optional key secret to use, defaults to that of the shared global key", ) ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{ passthroughArgs: true, }) if err != nil { return fmt.Errorf("parsing flags: %w", err) } daemonRPC, err := ctx.newDaemonRPC() if err != nil { return fmt.Errorf("creating daemon RPC client: %w", err) } defer daemonRPC.Close() clientParams, err := daemonRPC.GetGarageClientParams(ctx) if err != nil { return fmt.Errorf("calling GetGarageClientParams: %w", err) } s3APIAddr := clientParams.Node.S3APIAddr() if *keyID == "" { *keyID = clientParams.GlobalBucketS3APICredentials.ID } if *keySecret == "" { *keySecret = clientParams.GlobalBucketS3APICredentials.Secret } args := ctx.flags.Args() if i := ctx.flags.ArgsLenAtDash(); i >= 0 { args = args[i:] } envVars := daecommon.GetEnvVars() configDir, err := initMCConfigDir(envVars) if err != nil { return fmt.Errorf("initializing minio-client config directory: %w", err) } args = append([]string{ binPath("mc"), "--config-dir", configDir, }, args...) var ( mcHostVar = fmt.Sprintf( "MC_HOST_garage=http://%s:%s@%s", *keyID, *keySecret, s3APIAddr, ) binPath = binPath("mc") cliEnv = append( os.Environ(), mcHostVar, // The garage docs say this is necessary, though nothing bad // seems to happen if we leave it out *shrug* "MC_REGION=garage", ) ) if err := syscall.Exec(binPath, args, cliEnv); err != nil { return fmt.Errorf( "calling exec(%q, %#v, %#v): %w", binPath, args, cliEnv, err, ) } return nil }, } var subCmdGarageCLI = subCmd{ name: "cli", descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running isle daemon", do: func(ctx subCmdCtx) error { ctx, err := ctx.withParsedFlags(&withParsedFlagsOpts{ passthroughArgs: true, }) if err != nil { return fmt.Errorf("parsing flags: %w", err) } daemonRPC, err := ctx.newDaemonRPC() if err != nil { return fmt.Errorf("creating daemon RPC client: %w", err) } defer daemonRPC.Close() clientParams, err := daemonRPC.GetGarageClientParams(ctx) if err != nil { return fmt.Errorf("calling GetGarageClientParams: %w", err) } if clientParams.RPCSecret == "" { return errors.New("this host does not have the garage RPC secret") } var ( binPath = binPath("garage") args = append([]string{"garage"}, ctx.opts.args...) cliEnv = append( os.Environ(), "GARAGE_RPC_HOST="+clientParams.Node.RPCNodeAddr(), "GARAGE_RPC_SECRET="+clientParams.RPCSecret, ) ) if err := syscall.Exec(binPath, args, cliEnv); err != nil { return fmt.Errorf( "calling exec(%q, %#v, %#v): %w", binPath, args, cliEnv, err, ) } return nil }, } var subCmdGarage = subCmd{ name: "garage", descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running isle daemon", do: func(ctx subCmdCtx) error { return ctx.doSubCmd( subCmdGarageCLI, subCmdGarageMC, ) }, }