github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/defaultcontextstore.go (about)

     1  // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
     2  //go:build go1.19
     3  
     4  package command
     5  
     6  import (
     7  	"github.com/docker/docker/errdefs"
     8  	"github.com/khulnasoft/cli/cli/context/docker"
     9  	"github.com/khulnasoft/cli/cli/context/store"
    10  	cliflags "github.com/khulnasoft/cli/cli/flags"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  const (
    15  	// DefaultContextName is the name reserved for the default context (config & env based)
    16  	DefaultContextName = "default"
    17  
    18  	// EnvOverrideContext is the name of the environment variable that can be
    19  	// used to override the context to use. If set, it overrides the context
    20  	// that's set in the CLI's configuration file, but takes no effect if the
    21  	// "DOCKER_HOST" env-var is set (which takes precedence.
    22  	EnvOverrideContext = "DOCKER_CONTEXT"
    23  )
    24  
    25  // DefaultContext contains the default context data for all endpoints
    26  type DefaultContext struct {
    27  	Meta store.Metadata
    28  	TLS  store.ContextTLSData
    29  }
    30  
    31  // DefaultContextResolver is a function which resolves the default context base on the configuration and the env variables
    32  type DefaultContextResolver func() (*DefaultContext, error)
    33  
    34  // ContextStoreWithDefault implements the store.Store interface with a support for the default context
    35  type ContextStoreWithDefault struct {
    36  	store.Store
    37  	Resolver DefaultContextResolver
    38  }
    39  
    40  // EndpointDefaultResolver is implemented by any EndpointMeta object
    41  // which wants to be able to populate the store with whatever their default is.
    42  type EndpointDefaultResolver interface {
    43  	// ResolveDefault returns values suitable for storing in store.Metadata.Endpoints
    44  	// and store.ContextTLSData.Endpoints.
    45  	//
    46  	// An error is only returned for something fatal, not simply
    47  	// the lack of a default (e.g. because the config file which
    48  	// would contain it is missing). If there is no default then
    49  	// returns nil, nil, nil.
    50  	//
    51  	//nolint:dupword // ignore "Duplicate words (nil,) found"
    52  	ResolveDefault() (any, *store.EndpointTLSData, error)
    53  }
    54  
    55  // ResolveDefaultContext creates a Metadata for the current CLI invocation parameters
    56  func ResolveDefaultContext(opts *cliflags.ClientOptions, config store.Config) (*DefaultContext, error) {
    57  	contextTLSData := store.ContextTLSData{
    58  		Endpoints: make(map[string]store.EndpointTLSData),
    59  	}
    60  	contextMetadata := store.Metadata{
    61  		Endpoints: make(map[string]any),
    62  		Metadata: DockerContext{
    63  			Description: "",
    64  		},
    65  		Name: DefaultContextName,
    66  	}
    67  
    68  	dockerEP, err := resolveDefaultDockerEndpoint(opts)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP.EndpointMeta
    73  	if dockerEP.TLSData != nil {
    74  		contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerEP.TLSData.ToStoreTLSData()
    75  	}
    76  
    77  	if err := config.ForeachEndpointType(func(n string, get store.TypeGetter) error {
    78  		if n == docker.DockerEndpoint { // handled above
    79  			return nil
    80  		}
    81  		ep := get()
    82  		if i, ok := ep.(EndpointDefaultResolver); ok {
    83  			meta, tls, err := i.ResolveDefault()
    84  			if err != nil {
    85  				return err
    86  			}
    87  			if meta == nil {
    88  				return nil
    89  			}
    90  			contextMetadata.Endpoints[n] = meta
    91  			if tls != nil {
    92  				contextTLSData.Endpoints[n] = *tls
    93  			}
    94  		}
    95  		// Nothing to be done
    96  		return nil
    97  	}); err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	return &DefaultContext{Meta: contextMetadata, TLS: contextTLSData}, nil
   102  }
   103  
   104  // List implements store.Store's List
   105  func (s *ContextStoreWithDefault) List() ([]store.Metadata, error) {
   106  	contextList, err := s.Store.List()
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	defaultContext, err := s.Resolver()
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	return append(contextList, defaultContext.Meta), nil
   115  }
   116  
   117  // CreateOrUpdate is not allowed for the default context and fails
   118  func (s *ContextStoreWithDefault) CreateOrUpdate(meta store.Metadata) error {
   119  	if meta.Name == DefaultContextName {
   120  		return errdefs.InvalidParameter(errors.New("default context cannot be created nor updated"))
   121  	}
   122  	return s.Store.CreateOrUpdate(meta)
   123  }
   124  
   125  // Remove is not allowed for the default context and fails
   126  func (s *ContextStoreWithDefault) Remove(name string) error {
   127  	if name == DefaultContextName {
   128  		return errdefs.InvalidParameter(errors.New("default context cannot be removed"))
   129  	}
   130  	return s.Store.Remove(name)
   131  }
   132  
   133  // GetMetadata implements store.Store's GetMetadata
   134  func (s *ContextStoreWithDefault) GetMetadata(name string) (store.Metadata, error) {
   135  	if name == DefaultContextName {
   136  		defaultContext, err := s.Resolver()
   137  		if err != nil {
   138  			return store.Metadata{}, err
   139  		}
   140  		return defaultContext.Meta, nil
   141  	}
   142  	return s.Store.GetMetadata(name)
   143  }
   144  
   145  // ResetTLSMaterial is not implemented for default context and fails
   146  func (s *ContextStoreWithDefault) ResetTLSMaterial(name string, data *store.ContextTLSData) error {
   147  	if name == DefaultContextName {
   148  		return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
   149  	}
   150  	return s.Store.ResetTLSMaterial(name, data)
   151  }
   152  
   153  // ResetEndpointTLSMaterial is not implemented for default context and fails
   154  func (s *ContextStoreWithDefault) ResetEndpointTLSMaterial(contextName string, endpointName string, data *store.EndpointTLSData) error {
   155  	if contextName == DefaultContextName {
   156  		return errdefs.InvalidParameter(errors.New("default context cannot be edited"))
   157  	}
   158  	return s.Store.ResetEndpointTLSMaterial(contextName, endpointName, data)
   159  }
   160  
   161  // ListTLSFiles implements store.Store's ListTLSFiles
   162  func (s *ContextStoreWithDefault) ListTLSFiles(name string) (map[string]store.EndpointFiles, error) {
   163  	if name == DefaultContextName {
   164  		defaultContext, err := s.Resolver()
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  		tlsfiles := make(map[string]store.EndpointFiles)
   169  		for epName, epTLSData := range defaultContext.TLS.Endpoints {
   170  			var files store.EndpointFiles
   171  			for filename := range epTLSData.Files {
   172  				files = append(files, filename)
   173  			}
   174  			tlsfiles[epName] = files
   175  		}
   176  		return tlsfiles, nil
   177  	}
   178  	return s.Store.ListTLSFiles(name)
   179  }
   180  
   181  // GetTLSData implements store.Store's GetTLSData
   182  func (s *ContextStoreWithDefault) GetTLSData(contextName, endpointName, fileName string) ([]byte, error) {
   183  	if contextName == DefaultContextName {
   184  		defaultContext, err := s.Resolver()
   185  		if err != nil {
   186  			return nil, err
   187  		}
   188  		if defaultContext.TLS.Endpoints[endpointName].Files[fileName] == nil {
   189  			return nil, errdefs.NotFound(errors.Errorf("TLS data for %s/%s/%s does not exist", DefaultContextName, endpointName, fileName))
   190  		}
   191  		return defaultContext.TLS.Endpoints[endpointName].Files[fileName], nil
   192  	}
   193  	return s.Store.GetTLSData(contextName, endpointName, fileName)
   194  }
   195  
   196  // GetStorageInfo implements store.Store's GetStorageInfo
   197  func (s *ContextStoreWithDefault) GetStorageInfo(contextName string) store.StorageInfo {
   198  	if contextName == DefaultContextName {
   199  		return store.StorageInfo{MetadataPath: "<IN MEMORY>", TLSPath: "<IN MEMORY>"}
   200  	}
   201  	return s.Store.GetStorageInfo(contextName)
   202  }