github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/opts/secret.go (about)

     1  package opts
     2  
     3  import (
     4  	"encoding/csv"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  
    10  	swarmtypes "github.com/docker/docker/api/types/swarm"
    11  )
    12  
    13  // SecretOpt is a Value type for parsing secrets
    14  type SecretOpt struct {
    15  	values []*swarmtypes.SecretReference
    16  }
    17  
    18  // Set a new secret value
    19  func (o *SecretOpt) Set(value string) error {
    20  	csvReader := csv.NewReader(strings.NewReader(value))
    21  	fields, err := csvReader.Read()
    22  	if err != nil {
    23  		return err
    24  	}
    25  
    26  	options := &swarmtypes.SecretReference{
    27  		File: &swarmtypes.SecretReferenceFileTarget{
    28  			UID:  "0",
    29  			GID:  "0",
    30  			Mode: 0o444,
    31  		},
    32  	}
    33  
    34  	// support a simple syntax of --secret foo
    35  	if len(fields) == 1 && !strings.Contains(fields[0], "=") {
    36  		options.File.Name = fields[0]
    37  		options.SecretName = fields[0]
    38  		o.values = append(o.values, options)
    39  		return nil
    40  	}
    41  
    42  	for _, field := range fields {
    43  		key, val, ok := strings.Cut(field, "=")
    44  		if !ok || key == "" {
    45  			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
    46  		}
    47  		// TODO(thaJeztah): these options should not be case-insensitive.
    48  		switch strings.ToLower(key) {
    49  		case "source", "src":
    50  			options.SecretName = val
    51  		case "target":
    52  			options.File.Name = val
    53  		case "uid":
    54  			options.File.UID = val
    55  		case "gid":
    56  			options.File.GID = val
    57  		case "mode":
    58  			m, err := strconv.ParseUint(val, 0, 32)
    59  			if err != nil {
    60  				return fmt.Errorf("invalid mode specified: %v", err)
    61  			}
    62  
    63  			options.File.Mode = os.FileMode(m)
    64  		default:
    65  			return fmt.Errorf("invalid field in secret request: %s", key)
    66  		}
    67  	}
    68  
    69  	if options.SecretName == "" {
    70  		return fmt.Errorf("source is required")
    71  	}
    72  	if options.File.Name == "" {
    73  		options.File.Name = options.SecretName
    74  	}
    75  
    76  	o.values = append(o.values, options)
    77  	return nil
    78  }
    79  
    80  // Type returns the type of this option
    81  func (o *SecretOpt) Type() string {
    82  	return "secret"
    83  }
    84  
    85  // String returns a string repr of this option
    86  func (o *SecretOpt) String() string {
    87  	secrets := []string{}
    88  	for _, secret := range o.values {
    89  		repr := fmt.Sprintf("%s -> %s", secret.SecretName, secret.File.Name)
    90  		secrets = append(secrets, repr)
    91  	}
    92  	return strings.Join(secrets, ", ")
    93  }
    94  
    95  // Value returns the secret requests
    96  func (o *SecretOpt) Value() []*swarmtypes.SecretReference {
    97  	return o.values
    98  }