github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/tools/urls.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package tools 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 11 "github.com/juju/juju/environs" 12 conf "github.com/juju/juju/environs/config" 13 "github.com/juju/juju/environs/simplestreams" 14 "github.com/juju/juju/environs/storage" 15 envutils "github.com/juju/juju/environs/utils" 16 "github.com/juju/juju/juju/keys" 17 ) 18 19 type toolsDatasourceFuncId struct { 20 id string 21 f ToolsDataSourceFunc 22 } 23 24 var ( 25 toolsDatasourceFuncsMu sync.RWMutex 26 toolsDatasourceFuncs []toolsDatasourceFuncId 27 ) 28 29 // ToolsDataSourceFunc is a function type that takes an environment and 30 // returns a simplestreams datasource. 31 // 32 // ToolsDataSourceFunc will be used in GetMetadataSources. 33 // Any error satisfying errors.IsNotSupported will be ignored; 34 // any other error will be cause GetMetadataSources to fail. 35 type ToolsDataSourceFunc func(environs.Environ) (simplestreams.DataSource, error) 36 37 // RegisterToolsDataSourceFunc registers an ToolsDataSourceFunc 38 // with the specified id, overwriting any function previously registered 39 // with the same id. 40 func RegisterToolsDataSourceFunc(id string, f ToolsDataSourceFunc) { 41 toolsDatasourceFuncsMu.Lock() 42 defer toolsDatasourceFuncsMu.Unlock() 43 for i := range toolsDatasourceFuncs { 44 if toolsDatasourceFuncs[i].id == id { 45 toolsDatasourceFuncs[i].f = f 46 return 47 } 48 } 49 toolsDatasourceFuncs = append(toolsDatasourceFuncs, toolsDatasourceFuncId{id, f}) 50 } 51 52 // UnregisterToolsDataSourceFunc unregisters an ToolsDataSourceFunc 53 // with the specified id. 54 func UnregisterToolsDataSourceFunc(id string) { 55 toolsDatasourceFuncsMu.Lock() 56 defer toolsDatasourceFuncsMu.Unlock() 57 for i, f := range toolsDatasourceFuncs { 58 if f.id == id { 59 head := toolsDatasourceFuncs[:i] 60 tail := toolsDatasourceFuncs[i+1:] 61 toolsDatasourceFuncs = append(head, tail...) 62 return 63 } 64 } 65 } 66 67 // GetMetadataSources returns the sources to use when looking for 68 // simplestreams tools metadata for the given stream. 69 func GetMetadataSources(env environs.BootstrapEnviron, dataSourceFactory simplestreams.DataSourceFactory) ([]simplestreams.DataSource, error) { 70 config := env.Config() 71 72 // Add configured and environment-specific datasources. 73 var sources []simplestreams.DataSource 74 if userURL, ok := config.AgentMetadataURL(); ok { 75 dataSourceConfig := simplestreams.Config{ 76 Description: conf.AgentMetadataURLKey, 77 BaseURL: userURL, 78 PublicSigningKey: keys.JujuPublicKey, 79 HostnameVerification: config.SSLHostnameVerification(), 80 Priority: simplestreams.SPECIFIC_CLOUD_DATA, 81 } 82 if err := dataSourceConfig.Validate(); err != nil { 83 return nil, errors.Annotate(err, "simplestreams config validation failed") 84 } 85 dataSource := dataSourceFactory.NewDataSource(dataSourceConfig) 86 sources = append(sources, dataSource) 87 } 88 89 envDataSources, err := environmentDataSources(env) 90 if err != nil { 91 return nil, err 92 } 93 sources = append(sources, envDataSources...) 94 95 // Add the default, public datasource. 96 defaultURL, err := ToolsURL(DefaultBaseURL) 97 if err != nil { 98 return nil, err 99 } 100 if defaultURL != "" { 101 dataSourceConfig := simplestreams.Config{ 102 Description: "default simplestreams", 103 BaseURL: defaultURL, 104 PublicSigningKey: keys.JujuPublicKey, 105 HostnameVerification: true, 106 Priority: simplestreams.DEFAULT_CLOUD_DATA, 107 RequireSigned: DefaultBaseURL == streamsAgentURL, 108 } 109 if err := dataSourceConfig.Validate(); err != nil { 110 return nil, errors.Annotate(err, "simplestreams config validation failed") 111 } 112 dataSource := dataSourceFactory.NewDataSource(dataSourceConfig) 113 sources = append(sources, dataSource) 114 } 115 return sources, nil 116 } 117 118 // environmentDataSources returns simplestreams datasources for the environment 119 // by calling the functions registered in RegisterToolsDataSourceFunc. 120 // The datasources returned will be in the same order the functions were registered. 121 func environmentDataSources(bootstrapEnviron environs.BootstrapEnviron) ([]simplestreams.DataSource, error) { 122 toolsDatasourceFuncsMu.RLock() 123 defer toolsDatasourceFuncsMu.RUnlock() 124 125 var datasources []simplestreams.DataSource 126 env, ok := bootstrapEnviron.(environs.Environ) 127 if !ok { 128 logger.Debugf("environmentDataSources is supported for IAAS, environ %#v is not Environ", bootstrapEnviron) 129 // ignore for CAAS 130 return datasources, nil 131 } 132 for _, f := range toolsDatasourceFuncs { 133 logger.Debugf("trying datasource %q", f.id) 134 datasource, err := f.f(env) 135 if err != nil { 136 if errors.IsNotSupported(err) { 137 continue 138 } 139 return nil, err 140 } 141 datasources = append(datasources, datasource) 142 } 143 return datasources, nil 144 } 145 146 // ToolsURL returns a valid tools URL constructed from source. 147 // source may be a directory, or a URL like file://foo or http://foo. 148 func ToolsURL(source string) (string, error) { 149 if source == "" { 150 return "", nil 151 } 152 153 return envutils.GetURL(source, storage.BaseToolsPath) 154 }