github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/service/parse.go (about)

     1  package service
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/docker/docker/api/types"
     7  	"github.com/docker/docker/api/types/filters"
     8  	swarmtypes "github.com/docker/docker/api/types/swarm"
     9  	"github.com/docker/docker/client"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // ParseSecrets retrieves the secrets with the requested names and fills
    14  // secret IDs into the secret references.
    15  func ParseSecrets(ctx context.Context, apiClient client.SecretAPIClient, requestedSecrets []*swarmtypes.SecretReference) ([]*swarmtypes.SecretReference, error) {
    16  	if len(requestedSecrets) == 0 {
    17  		return []*swarmtypes.SecretReference{}, nil
    18  	}
    19  
    20  	secretRefs := make(map[string]*swarmtypes.SecretReference)
    21  
    22  	for _, secret := range requestedSecrets {
    23  		if _, exists := secretRefs[secret.File.Name]; exists {
    24  			return nil, errors.Errorf("duplicate secret target for %s not allowed", secret.SecretName)
    25  		}
    26  		secretRef := new(swarmtypes.SecretReference)
    27  		*secretRef = *secret
    28  		secretRefs[secret.File.Name] = secretRef
    29  	}
    30  
    31  	args := filters.NewArgs()
    32  	for _, s := range secretRefs {
    33  		args.Add("name", s.SecretName)
    34  	}
    35  
    36  	secrets, err := apiClient.SecretList(ctx, types.SecretListOptions{
    37  		Filters: args,
    38  	})
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	foundSecrets := make(map[string]string)
    44  	for _, secret := range secrets {
    45  		foundSecrets[secret.Spec.Annotations.Name] = secret.ID
    46  	}
    47  
    48  	addedSecrets := []*swarmtypes.SecretReference{}
    49  
    50  	for _, ref := range secretRefs {
    51  		id, ok := foundSecrets[ref.SecretName]
    52  		if !ok {
    53  			return nil, errors.Errorf("secret not found: %s", ref.SecretName)
    54  		}
    55  
    56  		// set the id for the ref to properly assign in swarm
    57  		// since swarm needs the ID instead of the name
    58  		ref.SecretID = id
    59  		addedSecrets = append(addedSecrets, ref)
    60  	}
    61  
    62  	return addedSecrets, nil
    63  }
    64  
    65  // ParseConfigs retrieves the configs from the requested names and converts
    66  // them to config references to use with the spec
    67  func ParseConfigs(ctx context.Context, apiClient client.ConfigAPIClient, requestedConfigs []*swarmtypes.ConfigReference) ([]*swarmtypes.ConfigReference, error) {
    68  	if len(requestedConfigs) == 0 {
    69  		return []*swarmtypes.ConfigReference{}, nil
    70  	}
    71  
    72  	// the configRefs map has two purposes: it prevents duplication of config
    73  	// target filenames. It is used to get all configs, so we can resolve
    74  	// their IDs. unfortunately, there are other targets for ConfigReferences,
    75  	// besides just a File; specifically, the Runtime target, which is used for
    76  	// CredentialSpecs. Therefore, we need to have a list of ConfigReferences
    77  	// that are not File targets as well. at this time of writing, the only use
    78  	// for Runtime targets is CredentialSpecs. However, to future-proof this
    79  	// functionality, we should handle the case where multiple Runtime targets
    80  	// are in use for the same Config, and we should deduplicate
    81  	// such ConfigReferences, as no matter how many times the Config is used,
    82  	// it is only needed to be referenced once.
    83  	configRefs := make(map[string]*swarmtypes.ConfigReference)
    84  	runtimeRefs := make(map[string]*swarmtypes.ConfigReference)
    85  
    86  	for _, config := range requestedConfigs {
    87  		// copy the config, so we don't mutate the args
    88  		configRef := new(swarmtypes.ConfigReference)
    89  		*configRef = *config
    90  
    91  		if config.Runtime != nil {
    92  			// by assigning to a map based on ConfigName, if the same Config
    93  			// is required as a Runtime target for multiple purposes, we only
    94  			// include it once in the final set of configs.
    95  			runtimeRefs[config.ConfigName] = config
    96  			// continue, so we skip the logic below for handling file-type
    97  			// configs
    98  			continue
    99  		}
   100  
   101  		if _, exists := configRefs[config.File.Name]; exists {
   102  			return nil, errors.Errorf("duplicate config target for %s not allowed", config.ConfigName)
   103  		}
   104  
   105  		configRefs[config.File.Name] = configRef
   106  	}
   107  
   108  	args := filters.NewArgs()
   109  	for _, s := range configRefs {
   110  		args.Add("name", s.ConfigName)
   111  	}
   112  	for _, s := range runtimeRefs {
   113  		args.Add("name", s.ConfigName)
   114  	}
   115  
   116  	configs, err := apiClient.ConfigList(ctx, types.ConfigListOptions{
   117  		Filters: args,
   118  	})
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	foundConfigs := make(map[string]string)
   124  	for _, config := range configs {
   125  		foundConfigs[config.Spec.Annotations.Name] = config.ID
   126  	}
   127  
   128  	addedConfigs := []*swarmtypes.ConfigReference{}
   129  
   130  	for _, ref := range configRefs {
   131  		id, ok := foundConfigs[ref.ConfigName]
   132  		if !ok {
   133  			return nil, errors.Errorf("config not found: %s", ref.ConfigName)
   134  		}
   135  
   136  		// set the id for the ref to properly assign in swarm
   137  		// since swarm needs the ID instead of the name
   138  		ref.ConfigID = id
   139  		addedConfigs = append(addedConfigs, ref)
   140  	}
   141  
   142  	// unfortunately, because the key of configRefs and runtimeRefs is different
   143  	// values that may collide, we can't just do some fancy trickery to
   144  	// concat maps, we need to do two separate loops
   145  	for _, ref := range runtimeRefs {
   146  		id, ok := foundConfigs[ref.ConfigName]
   147  		if !ok {
   148  			return nil, errors.Errorf("config not found: %s", ref.ConfigName)
   149  		}
   150  
   151  		ref.ConfigID = id
   152  		addedConfigs = append(addedConfigs, ref)
   153  	}
   154  
   155  	return addedConfigs, nil
   156  }