sigs.k8s.io/cluster-api@v1.6.3/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 errno 44 import json 45 import os 46 import subprocess 47 from distutils.dir_util import copy_tree 48 from distutils.file_util import copy_file 49 50 settings = {} 51 52 providers = { 53 'cluster-api': { 54 'componentsFile': 'core-components.yaml', 55 'nextVersion': 'v1.6.99', 56 'type': 'CoreProvider', 57 }, 58 'bootstrap-kubeadm': { 59 'componentsFile': 'bootstrap-components.yaml', 60 'nextVersion': 'v1.6.99', 61 'type': 'BootstrapProvider', 62 'configFolder': 'bootstrap/kubeadm/config/default', 63 }, 64 'control-plane-kubeadm': { 65 'componentsFile': 'control-plane-components.yaml', 66 'nextVersion': 'v1.6.99', 67 'type': 'ControlPlaneProvider', 68 'configFolder': 'controlplane/kubeadm/config/default', 69 }, 70 'infrastructure-docker': { 71 'componentsFile': 'infrastructure-components.yaml', 72 'nextVersion': 'v1.6.99', 73 'type': 'InfrastructureProvider', 74 'configFolder': 'test/infrastructure/docker/config/default', 75 }, 76 'infrastructure-in-memory': { 77 'componentsFile': 'infrastructure-components.yaml', 78 'nextVersion': 'v1.6.99', 79 'type': 'InfrastructureProvider', 80 'configFolder': 'test/infrastructure/inmemory/config/default', 81 }, 82 'runtime-extension-test': { 83 'componentsFile': 'runtime-extension-components.yaml', 84 'nextVersion': 'v1.6.99', 85 'type': 'RuntimeExtensionProvider', 86 'configFolder': 'test/extension/config/default', 87 }, 88 } 89 90 91 def load_settings(): 92 global settings 93 try: 94 settings = json.load(open('clusterctl-settings.json')) 95 except Exception as e: 96 raise Exception('failed to load clusterctl-settings.json: {}'.format(e)) 97 98 99 def load_providers(): 100 provider_repos = settings.get('provider_repos', []) 101 for repo in provider_repos: 102 file = repo + '/clusterctl-settings.json' 103 try: 104 provider_details = json.load(open(file)) 105 provider_name = provider_details['name'] 106 provider_config = provider_details['config'] 107 provider_config['repo'] = repo 108 providers[provider_name] = provider_config 109 except Exception as e: 110 raise Exception('failed to load clusterctl-settings.json from repo {}: {}'.format(repo, e)) 111 112 113 def execCmd(args): 114 try: 115 out = subprocess.Popen(args, 116 stdout=subprocess.PIPE, 117 stderr=subprocess.STDOUT) 118 119 stdout, stderr = out.communicate() 120 if stderr is not None: 121 raise Exception('stderr contains: \n{}'.format(stderr)) 122 123 return stdout 124 except Exception as e: 125 raise Exception('failed to run {}: {}'.format(args, e)) 126 127 128 def get_repository_folder(): 129 config_dir = os.getenv("XDG_CONFIG_HOME", "") 130 if config_dir == "": 131 home_dir = os.getenv("HOME", "") 132 if home_dir == "": 133 raise Exception('HOME variable is not set') 134 config_dir = os.path.join(home_dir, ".config") 135 return os.path.join(config_dir, 'cluster-api', 'dev-repository') 136 137 138 def write_local_repository(provider, version, components_file, components_yaml, metadata_file): 139 try: 140 repository_folder = get_repository_folder() 141 provider_folder = os.path.join(repository_folder, provider, version) 142 try: 143 os.makedirs(provider_folder) 144 except OSError as e: 145 if e.errno != errno.EEXIST: 146 raise 147 components_path = os.path.join(provider_folder, components_file) 148 f = open(components_path, 'wb') 149 f.write(components_yaml) 150 f.close() 151 152 copy_file(metadata_file, provider_folder) 153 154 if provider == "infrastructure-docker": 155 copy_tree("test/infrastructure/docker/templates", provider_folder) 156 157 if provider == "infrastructure-in-memory": 158 copy_tree("test/infrastructure/inmemory/templates", provider_folder) 159 160 return components_path 161 except Exception as e: 162 raise Exception('failed to write {} to {}: {}'.format(components_file, provider_folder, e)) 163 164 165 def create_local_repositories(): 166 providerList = settings.get('providers', []) 167 assert providerList is not None, 'invalid configuration: please define the list of providers to override' 168 assert len(providerList)>0, 'invalid configuration: please define at least one provider to override' 169 170 for provider in providerList: 171 p = providers.get(provider) 172 assert p is not None, 'invalid configuration: please specify the configuration for the {} provider'.format( 173 provider) 174 175 repo = p.get('repo', '.') 176 config_folder = p.get('configFolder', 'config/default') 177 metadata_file = repo + '/metadata.yaml' 178 179 next_version = p.get('nextVersion') 180 assert next_version is not None, 'invalid configuration for provider {}: please provide nextVersion value'.format( 181 provider) 182 183 name, type = splitNameAndType(provider) 184 assert name is not None, 'invalid configuration for provider {}: please use a valid provider label'.format( 185 provider) 186 187 components_file = p.get('componentsFile') 188 assert components_file is not None, 'invalid configuration for provider {}: please provide componentsFile value'.format( 189 provider) 190 191 execCmd(['make', 'kustomize']) 192 components_yaml = execCmd(['./hack/tools/bin/kustomize', 'build', os.path.join(repo, config_folder)]) 193 components_path = write_local_repository(provider, next_version, components_file, components_yaml, 194 metadata_file) 195 196 yield name, type, next_version, components_path 197 198 199 def injectLatest(path): 200 head, tail = os.path.split(path) 201 return '{}/latest/{}'.format(head, tail) 202 203 204 def create_dev_config(repos): 205 yaml = "providers:\n" 206 for name, type, next_version, components_path in repos: 207 yaml += "- name: \"{}\"\n".format(name) 208 yaml += " type: \"{}\"\n".format(type) 209 yaml += " url: \"{}\"\n".format(components_path) 210 yaml += "overridesFolder: \"{}/overrides\"\n".format(get_repository_folder()) 211 212 try: 213 repository_folder = get_repository_folder() 214 config_path = os.path.join(repository_folder, "config.yaml") 215 f = open(config_path, 'w') 216 f.write(yaml) 217 f.close() 218 return components_path 219 except Exception as e: 220 raise Exception('failed to write {}: {}'.format(config_path, e)) 221 222 223 def splitNameAndType(provider): 224 if provider == 'cluster-api': 225 return 'cluster-api', 'CoreProvider' 226 if provider.startswith('bootstrap-'): 227 return provider[len('bootstrap-'):], 'BootstrapProvider' 228 if provider.startswith('control-plane-'): 229 return provider[len('control-plane-'):], 'ControlPlaneProvider' 230 if provider.startswith('infrastructure-'): 231 return provider[len('infrastructure-'):], 'InfrastructureProvider' 232 if provider.startswith('ipam-'): 233 return provider[len('ipam-'):], 'IPAMProvider' 234 if provider.startswith('runtime-extension-'): 235 return provider[len('runtime-extension-'):], 'RuntimeExtensionProvider' 236 if provider.startswith('addon-'): 237 return provider[len('addon-'):], 'AddonProvider' 238 return None, None 239 240 241 def CoreProviderFlag(): 242 return '--core' 243 244 245 def BootstrapProviderFlag(): 246 return '--bootstrap' 247 248 249 def ControlPlaneProviderFlag(): 250 return '--control-plane' 251 252 253 def InfrastructureProviderFlag(): 254 return '--infrastructure' 255 256 257 def IPAMProviderFlag(): 258 return '--ipam' 259 260 261 def RuntimeExtensionProviderFlag(): 262 return '--runtime-extension' 263 264 265 def AddonProviderFlag(): 266 return '--addon' 267 268 269 def type_to_flag(type): 270 switcher = { 271 'CoreProvider': CoreProviderFlag, 272 'BootstrapProvider': BootstrapProviderFlag, 273 'ControlPlaneProvider': ControlPlaneProviderFlag, 274 'InfrastructureProvider': InfrastructureProviderFlag, 275 'IPAMProvider': IPAMProviderFlag, 276 'RuntimeExtensionProvider': RuntimeExtensionProviderFlag, 277 'AddonProvider': AddonProviderFlag 278 } 279 func = switcher.get(type, lambda: 'Invalid type') 280 return func() 281 282 283 def print_instructions(repos): 284 providerList = settings.get('providers', []) 285 print('clusterctl local overrides generated from local repositories for the {} providers.'.format( 286 ', '.join(providerList))) 287 print('in order to use them, please run:') 288 print 289 cmd = "clusterctl init \\\n" 290 for name, type, next_version, components_path in repos: 291 cmd += " {} {}:{} \\\n".format(type_to_flag(type), name, next_version) 292 cmd += " --config $XDG_CONFIG_HOME/cluster-api/dev-repository/config.yaml" 293 print(cmd) 294 print 295 if 'infrastructure-docker' in providerList: 296 print('please check the documentation for additional steps required for using the docker provider') 297 print 298 if 'infrastructure-in-memory' in providerList: 299 print ('please check the documentation for additional steps required for using the in-memory provider') 300 print 301 302 303 load_settings() 304 305 load_providers() 306 307 repos = list(create_local_repositories()) 308 309 create_dev_config(repos) 310 311 print_instructions(repos)