github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/imagemetadata.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environs
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  
    11  	"github.com/juju/juju/environs/imagemetadata"
    12  	"github.com/juju/juju/environs/simplestreams"
    13  )
    14  
    15  type datasourceFuncId struct {
    16  	id string
    17  	f  ImageDataSourceFunc
    18  }
    19  
    20  var (
    21  	datasourceFuncsMu sync.RWMutex
    22  	datasourceFuncs   []datasourceFuncId
    23  )
    24  
    25  // ImageDataSourceFunc is a function type that takes an environment and
    26  // returns a simplestreams datasource.
    27  //
    28  // ImageDataSourceFunc will be used in ImageMetadataSources.
    29  // Any error satisfying errors.IsNotSupported will be ignored;
    30  // any other error will be cause ImageMetadataSources to fail.
    31  type ImageDataSourceFunc func(Environ) (simplestreams.DataSource, error)
    32  
    33  // RegisterUserImageDataSourceFunc registers an ImageDataSourceFunc
    34  // with the specified id at the start of the search path, overwriting
    35  // any function previously registered with the same id.
    36  func RegisterUserImageDataSourceFunc(id string, f ImageDataSourceFunc) {
    37  	datasourceFuncsMu.Lock()
    38  	defer datasourceFuncsMu.Unlock()
    39  	for i := range datasourceFuncs {
    40  		if datasourceFuncs[i].id == id {
    41  			datasourceFuncs[i].f = f
    42  			return
    43  		}
    44  	}
    45  	logger.Debugf("new user image datasource registered: %v", id)
    46  	datasourceFuncs = append([]datasourceFuncId{{id, f}}, datasourceFuncs...)
    47  }
    48  
    49  // RegisterImageDataSourceFunc registers an ImageDataSourceFunc
    50  // with the specified id, overwriting any function previously registered
    51  // with the same id.
    52  func RegisterImageDataSourceFunc(id string, f ImageDataSourceFunc) {
    53  	datasourceFuncsMu.Lock()
    54  	defer datasourceFuncsMu.Unlock()
    55  	for i := range datasourceFuncs {
    56  		if datasourceFuncs[i].id == id {
    57  			datasourceFuncs[i].f = f
    58  			return
    59  		}
    60  	}
    61  	logger.Debugf("new model image datasource registered: %v", id)
    62  	datasourceFuncs = append(datasourceFuncs, datasourceFuncId{id, f})
    63  }
    64  
    65  // UnregisterImageDataSourceFunc unregisters an ImageDataSourceFunc
    66  // with the specified id.
    67  func UnregisterImageDataSourceFunc(id string) {
    68  	datasourceFuncsMu.Lock()
    69  	defer datasourceFuncsMu.Unlock()
    70  	for i, f := range datasourceFuncs {
    71  		if f.id == id {
    72  			head := datasourceFuncs[:i]
    73  			tail := datasourceFuncs[i+1:]
    74  			datasourceFuncs = append(head, tail...)
    75  			return
    76  		}
    77  	}
    78  }
    79  
    80  // ImageMetadataSources returns the sources to use when looking for
    81  // simplestreams image id metadata for the given stream.
    82  func ImageMetadataSources(env BootstrapEnviron, dataSourceFactory simplestreams.DataSourceFactory) ([]simplestreams.DataSource, error) {
    83  	config := env.Config()
    84  
    85  	// Add configured and environment-specific datasources.
    86  	var sources []simplestreams.DataSource
    87  	if userURL, ok := config.ImageMetadataURL(); ok {
    88  		publicKey, err := simplestreams.UserPublicSigningKey()
    89  		if err != nil {
    90  			return nil, errors.Trace(err)
    91  		}
    92  		cfg := simplestreams.Config{
    93  			Description:          "image-metadata-url",
    94  			BaseURL:              userURL,
    95  			PublicSigningKey:     publicKey,
    96  			HostnameVerification: config.SSLHostnameVerification(),
    97  			Priority:             simplestreams.SPECIFIC_CLOUD_DATA,
    98  		}
    99  		if err := cfg.Validate(); err != nil {
   100  			return nil, errors.Trace(err)
   101  		}
   102  		dataSource := dataSourceFactory.NewDataSource(cfg)
   103  		sources = append(sources, dataSource)
   104  	}
   105  
   106  	envDataSources, err := environmentDataSources(env)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	sources = append(sources, envDataSources...)
   111  
   112  	if config.ImageMetadataDefaultsDisabled() {
   113  		logger.Debugf("default image metadata sources are disabled")
   114  	} else {
   115  		// Add the official image metadata datasources.
   116  		officialDataSources, err := imagemetadata.OfficialDataSources(dataSourceFactory, config.ImageStream())
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  		sources = append(sources, officialDataSources...)
   121  	}
   122  
   123  	for _, ds := range sources {
   124  		logger.Debugf("obtained image datasource %q", ds.Description())
   125  	}
   126  	return sources, nil
   127  }
   128  
   129  // environmentDataSources returns simplestreams datasources for the environment
   130  // by calling the functions registered in RegisterImageDataSourceFunc.
   131  // The datasources returned will be in the same order the functions were registered.
   132  func environmentDataSources(bootstrapEnviron BootstrapEnviron) ([]simplestreams.DataSource, error) {
   133  	datasourceFuncsMu.RLock()
   134  	defer datasourceFuncsMu.RUnlock()
   135  	var datasources []simplestreams.DataSource
   136  	env, ok := bootstrapEnviron.(Environ)
   137  	if !ok {
   138  		logger.Debugf("environmentDataSources is supported for IAAS, environ %#v is not Environ", bootstrapEnviron)
   139  		// ignore for CAAS
   140  		return datasources, nil
   141  	}
   142  	for _, f := range datasourceFuncs {
   143  		datasource, err := f.f(env)
   144  		if err != nil {
   145  			if errors.IsNotSupported(err) {
   146  				continue
   147  			}
   148  			return nil, err
   149  		}
   150  		datasources = append(datasources, datasource)
   151  	}
   152  	return datasources, nil
   153  }