sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/hack/create-local-repository.py (about) 1 #!/usr/bin/env python3 2 3 # Copyright 2020 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 ################### 18 19 # create-local-repository.py takes in input a list of provider and, for each of them, generates the components YAML from the 20 # local repositories (the GitHub repositories clone), and finally stores it in the clusterctl local override folder 21 22 # prerequisites: 23 24 # - the script should be executed from sigs.k8s.io/cluster-api/ by calling cmd/clusterctl/hack/create-local-repository.py 25 # - there should be a sigs.k8s.io/cluster-api/clusterctl-settings.json file with the list of provider for which 26 # the local overrides should be generated and the list of provider repositories to be included (on top of cluster-api). 27 # { 28 # "providers": [ "cluster-api", "bootstrap-kubeadm", "infrastructure-aws"], 29 # "provider_repos": ["../cluster-api-provider-aws"] 30 # } 31 # - for each additional provider repository there should be a sigs.k8s.io/<provider_repo>/clusterctl-settings.json file e.g. 32 # { 33 # "name": "infrastructure-aws", 34 # "config": { 35 # "componentsFile": "infrastructure-components.yaml", 36 # "nextVersion": "v0.5.0", 37 # } 38 39 ################### 40 41 from __future__ import unicode_literals 42 43 import sys 44 import errno 45 import json 46 import os 47 import subprocess 48 import urllib.request 49 from distutils.dir_util import copy_tree 50 from distutils.file_util import copy_file 51 52 settings = {} 53 54 providers = { 55 'cluster-api': { 56 'componentsFile': 'core-components.yaml', 57 'nextVersion': 'v1.7.99', 58 'type': 'CoreProvider', 59 }, 60 'bootstrap-kubeadm': { 61 'componentsFile': 'bootstrap-components.yaml', 62 'nextVersion': 'v1.7.99', 63 'type': 'BootstrapProvider', 64 'configFolder': 'bootstrap/kubeadm/config/default', 65 }, 66 'control-plane-kubeadm': { 67 'componentsFile': 'control-plane-components.yaml', 68 'nextVersion': 'v1.7.99', 69 'type': 'ControlPlaneProvider', 70 'configFolder': 'controlplane/kubeadm/config/default', 71 }, 72 'infrastructure-docker': { 73 'componentsFile': 'infrastructure-components-development.yaml', 74 'nextVersion': 'v1.7.99', 75 'type': 'InfrastructureProvider', 76 'configFolder': 'test/infrastructure/docker/config/default', 77 }, 78 'infrastructure-in-memory': { 79 'componentsFile': 'infrastructure-components-in-memory-development.yaml', 80 'nextVersion': 'v1.7.99', 81 'type': 'InfrastructureProvider', 82 'configFolder': 'test/infrastructure/inmemory/config/default', 83 }, 84 'runtime-extension-test': { 85 'componentsFile': 'runtime-extension-components-development.yaml', 86 'nextVersion': 'v1.7.99', 87 'type': 'RuntimeExtensionProvider', 88 'configFolder': 'test/extension/config/default', 89 }, 90 } 91 92 93 def load_settings(): 94 global settings 95 try: 96 settings = json.load(open('clusterctl-settings.json')) 97 except Exception as e: 98 raise Exception('failed to load clusterctl-settings.json: {}'.format(e)) 99 100 101 def load_providers(): 102 provider_repos = settings.get('provider_repos', []) 103 for repo in provider_repos: 104 file = repo + '/clusterctl-settings.json' 105 try: 106 provider_details = json.load(open(file)) 107 provider_name = provider_details['name'] 108 provider_config = provider_details['config'] 109 provider_config['repo'] = repo 110 providers[provider_name] = provider_config 111 except Exception as e: 112 raise Exception('failed to load clusterctl-settings.json from repo {}: {}'.format(repo, e)) 113 114 115 def execCmd(args): 116 try: 117 out = subprocess.Popen(args, 118 stdout=subprocess.PIPE, 119 stderr=subprocess.STDOUT) 120 121 stdout, stderr = out.communicate() 122 if stderr is not None: 123 raise Exception('stderr contains: \n{}'.format(stderr)) 124 125 return stdout 126 except Exception as e: 127 raise Exception('failed to run {}: {}'.format(args, e)) 128 129 130 def get_repository_folder(): 131 config_dir = os.getenv("XDG_CONFIG_HOME", "") 132 if config_dir == "": 133 home_dir = os.getenv("HOME", "") 134 if home_dir == "": 135 raise Exception('HOME variable is not set') 136 config_dir = os.path.join(home_dir, ".config") 137 return os.path.join(config_dir, 'cluster-api', 'dev-repository') 138 139 140 def write_local_repository(provider, version, components_file, components_yaml, metadata_file): 141 try: 142 repository_folder = get_repository_folder() 143 provider_folder = os.path.join(repository_folder, provider, version) 144 try: 145 os.makedirs(provider_folder) 146 except OSError as e: 147 if e.errno != errno.EEXIST: 148 raise 149 components_path = os.path.join(provider_folder, components_file) 150 f = open(components_path, 'wb') 151 f.write(components_yaml) 152 f.close() 153 154 copy_file(metadata_file, provider_folder) 155 156 if provider == "infrastructure-docker": 157 copy_tree("test/infrastructure/docker/templates", provider_folder) 158 159 if provider == "infrastructure-in-memory": 160 copy_tree("test/infrastructure/inmemory/templates", provider_folder) 161 162 return components_path 163 except Exception as e: 164 raise Exception('failed to write {} to {}: {}'.format(components_file, provider_folder, e)) 165 166 167 def create_local_repositories(): 168 providerList = settings.get('providers', []) 169 assert providerList is not None, 'invalid configuration: please define the list of providers to override' 170 assert len(providerList)>0, 'invalid configuration: please define at least one provider to override' 171 172 if len(sys.argv) == 1: 173 execCmd(['make', 'kustomize']) 174 175 for provider in providerList: 176 p = providers.get(provider) 177 assert p is not None, 'invalid configuration: please specify the configuration for the {} provider'.format( 178 provider) 179 180 repo = p.get('repo', '.') 181 config_folder = p.get('configFolder', 'config/default') 182 metadata_file = repo + '/metadata.yaml' 183 184 next_version = p.get('nextVersion') 185 assert next_version is not None, 'invalid configuration for provider {}: please provide nextVersion value'.format( 186 provider) 187 188 name, type = splitNameAndType(provider) 189 assert name is not None, 'invalid configuration for provider {}: please use a valid provider label'.format( 190 provider) 191 192 components_file = p.get('componentsFile') 193 assert components_file is not None, 'invalid configuration for provider {}: please provide componentsFile value'.format( 194 provider) 195 196 if len(sys.argv) > 1: 197 url = "{}/{}".format(sys.argv[1], components_file) 198 components_yaml = urllib.request.urlopen(url).read() 199 else: 200 components_yaml = execCmd(['./hack/tools/bin/kustomize', 'build', os.path.join(repo, config_folder)]) 201 202 components_path = write_local_repository(provider, next_version, components_file, components_yaml, 203 metadata_file) 204 205 yield name, type, next_version, components_path 206 207 208 def injectLatest(path): 209 head, tail = os.path.split(path) 210 return '{}/latest/{}'.format(head, tail) 211 212 213 def create_dev_config(repos): 214 yaml = "providers:\n" 215 for name, type, next_version, components_path in repos: 216 yaml += "- name: \"{}\"\n".format(name) 217 yaml += " type: \"{}\"\n".format(type) 218 yaml += " url: \"{}\"\n".format(components_path) 219 yaml += "overridesFolder: \"{}/overrides\"\n".format(get_repository_folder()) 220 221 try: 222 repository_folder = get_repository_folder() 223 config_path = os.path.join(repository_folder, "config.yaml") 224 f = open(config_path, 'w') 225 f.write(yaml) 226 f.close() 227 return components_path 228 except Exception as e: 229 raise Exception('failed to write {}: {}'.format(config_path, e)) 230 231 232 def splitNameAndType(provider): 233 if provider == 'cluster-api': 234 return 'cluster-api', 'CoreProvider' 235 if provider.startswith('bootstrap-'): 236 return provider[len('bootstrap-'):], 'BootstrapProvider' 237 if provider.startswith('control-plane-'): 238 return provider[len('control-plane-'):], 'ControlPlaneProvider' 239 if provider.startswith('infrastructure-'): 240 return provider[len('infrastructure-'):], 'InfrastructureProvider' 241 if provider.startswith('ipam-'): 242 return provider[len('ipam-'):], 'IPAMProvider' 243 if provider.startswith('runtime-extension-'): 244 return provider[len('runtime-extension-'):], 'RuntimeExtensionProvider' 245 if provider.startswith('addon-'): 246 return provider[len('addon-'):], 'AddonProvider' 247 return None, None 248 249 250 def CoreProviderFlag(): 251 return '--core' 252 253 254 def BootstrapProviderFlag(): 255 return '--bootstrap' 256 257 258 def ControlPlaneProviderFlag(): 259 return '--control-plane' 260 261 262 def InfrastructureProviderFlag(): 263 return '--infrastructure' 264 265 266 def IPAMProviderFlag(): 267 return '--ipam' 268 269 270 def RuntimeExtensionProviderFlag(): 271 return '--runtime-extension' 272 273 274 def AddonProviderFlag(): 275 return '--addon' 276 277 278 def type_to_flag(type): 279 switcher = { 280 'CoreProvider': CoreProviderFlag, 281 'BootstrapProvider': BootstrapProviderFlag, 282 'ControlPlaneProvider': ControlPlaneProviderFlag, 283 'InfrastructureProvider': InfrastructureProviderFlag, 284 'IPAMProvider': IPAMProviderFlag, 285 'RuntimeExtensionProvider': RuntimeExtensionProviderFlag, 286 'AddonProvider': AddonProviderFlag 287 } 288 func = switcher.get(type, lambda: 'Invalid type') 289 return func() 290 291 292 def print_instructions(repos): 293 providerList = settings.get('providers', []) 294 print('clusterctl local overrides generated from local repositories for the {} providers.'.format( 295 ', '.join(providerList))) 296 print('in order to use them, please run:') 297 print 298 cmd = "clusterctl init \\\n" 299 for name, type, next_version, components_path in repos: 300 cmd += " {} {}:{} \\\n".format(type_to_flag(type), name, next_version) 301 config_dir = os.getenv("XDG_CONFIG_HOME", "") 302 if config_dir != "": 303 cmd += " --config $XDG_CONFIG_HOME/cluster-api/dev-repository/config.yaml" 304 else: 305 cmd += " --config $HOME/.config/cluster-api/dev-repository/config.yaml" 306 print(cmd) 307 print 308 if 'infrastructure-docker' in providerList: 309 print('please check the documentation for additional steps required for using the docker provider') 310 print 311 if 'infrastructure-in-memory' in providerList: 312 print ('please check the documentation for additional steps required for using the in-memory provider') 313 print 314 315 316 load_settings() 317 318 load_providers() 319 320 repos = list(create_local_repositories()) 321 322 create_dev_config(repos) 323 324 print_instructions(repos)