Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨clusterctl: implements hack for generating local overrides #2027

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ vendor
tilt.d
tilt-settings.json
.tiltbuild

# User-supplied clusterctl hacks settings
clusterctl-settings.json
181 changes: 181 additions & 0 deletions cmd/clusterctl/hack/local-overrides.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/usr/bin/env python

# Copyright 2020 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

###################

# local-overrides.py takes in input a list of provider and, for each of them, generates the components YAML from the
# local repositories (the GitHub repositories clone), and finally stores it in the clusterctl local override folder

# prerequisites:

# - the script should be executed from sigs.k8s.io/cluster-api/ by calling cmd/clusterctl/hack/local-overrides.py
# - there should be a sigs.k8s.io/cluster-api/clusterctl-settings.json file with the list of provider for which
# the local overrides should be generated and the list of provider repositories to be included (on top of cluster-api).
# {
# "providers": [ "cluster-api", "kubeadm-bootstrap", "aws"],
# "provider_repos": ["../cluster-api-provider-aws"]
# }
# - for each additional provider repository there should be a sigs.k8s.io/<provider_repo>/clusterctl-settings.json file e.g.
# {
# "name": "aws",
# "config": {
# "componentsFile": "infrastructure-components.yaml",
# "nextVersion": "v0.5.0",
# "type": "InfrastructureProvider"
# }

###################

import json
import subprocess
import os
import errno

settings = {}

providers = {
'cluster-api': {
'componentsFile': 'core-components.yaml',
'nextVersion': 'v0.3.0',
'type': 'CoreProvider',
},
'kubeadm-bootstrap': {
'componentsFile': 'bootstrap-components.yaml',
'nextVersion': 'v0.3.0',
'type': 'BootstrapProvider',
'configFolder': '/bootstrap/kubeadm/config/default',
},
}

validTypes = ['CoreProvider','BootstrapProvider','InfrastructureProvider']

def load_settings():
global settings
try:
settings = json.load(open('clusterctl-settings.json'))
except Exception as e:
raise Exception('failed to load clusterctl-settings.json: {}'.format(e))

def load_providers():
provider_repos = settings.get('provider_repos', [])
for repo in provider_repos:
file = repo + '/clusterctl-settings.json'
try:
provider_details = json.load(open(file))
provider_name = provider_details['name']
provider_config = provider_details['config']
provider_config['repo'] = repo
providers[provider_name] = provider_config
except Exception as e:
raise Exception('failed to load clusterctl-settings.json from repo {}: {}'.format(repo, e))

def execCmd(args):
try:
out = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)

stdout, stderr = out.communicate()
if stderr is not None:
raise Exception('stderr contains: \n{}'.format(stderr))

return stdout
except Exception as e:
raise Exception('failed to run {}: {}'.format(args, e))

def get_home():
return os.path.expanduser('~')

def write_local_override(provider, version, components_file, components_yaml):
try:
home = get_home()
overrides_folder = os.path.join(home, 'cluster-api', 'overrides')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd want this in a dotfolder, like .cluster-api, right?

Copy link
Member Author

@fabriziopandini fabriziopandini Jan 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CAEP the consensus was for $HOME/cluster-api/.clusterctl.yaml
If you are ok I will be happy to change it into $HOME/.cluster-api/clusterctl.yaml in a follow-up PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$HOME/cluster-api feels very wrong to me. Follow-up is fine, but let's remember to do it 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huge +1, the folder should be hidden

provider_overrides_folder = os.path.join(overrides_folder, provider, version)
try:
os.makedirs(provider_overrides_folder)
except OSError as e:
if e.errno != errno.EEXIST:
raise
f = open(os.path.join(provider_overrides_folder, components_file), 'w')
f.write(components_yaml)
f.close()
except Exception as e:
raise Exception('failed to write {} to {}: {}'.format(components_file, components_folder, e))

def create_local_overrides():
providerList = settings.get('providers', [])
assert providerList is not None, 'invalid configuration: please define the list of providers to override'
assert len(providerList)>0, 'invalid configuration: please define at least one provider to override'

for provider in providerList:
p = providers.get(provider)
assert p is not None, 'invalid configuration: please specify the configuration for the {} provider'.format(provider)

repo = p.get('repo', '.')
config_folder = p.get('configFolder', '/config/default')

next_version = p.get('nextVersion')
assert next_version is not None, 'invalid configuration for provider {}: please provide nextVersion value'.format(provider)

type = p.get('type')
assert type is not None, 'invalid configuration for provider {}: please provide type value'.format(provider)
assert type in validTypes, 'invalid configuration for provider {}: please use one of {}'.format(provider, ', '.join(validTypes))

components_file = p.get('componentsFile')
assert components_file is not None, 'invalid configuration for provider {}: please provide componentsFile value'.format(provider)

components_yaml = execCmd(['kustomize', 'build', repo + config_folder])
write_local_override(provider, next_version, components_file, components_yaml)

yield provider, type, next_version
ncdc marked this conversation as resolved.
Show resolved Hide resolved


def CoreProviderFlag():
return '--core'

def BootstrapProviderFlag():
return '--bootstrap'

def InfrastructureProviderFlag():
return '--infrastructure'

def type_to_flag(type):
switcher = {
'CoreProvider': CoreProviderFlag,
'BootstrapProvider': BootstrapProviderFlag,
'InfrastructureProvider': InfrastructureProviderFlag
}
func = switcher.get(type, lambda: 'Invalid type')
return func()

def print_instructions(overrides):
providerList = settings.get('providers', [])
print ('clusterctl local overrides generated from local repositories for the {} providers.'.format(', '.join(providerList)))
print ('in order to use them, please run:')
print
cmd = 'clusterctl init'
for provider, type, next_version in overrides:
cmd += ' {} {}:{}'.format(type_to_flag(type), provider, next_version)
print (cmd)
print

load_settings()

load_providers()

overrides = create_local_overrides()

print_instructions(overrides)
11 changes: 5 additions & 6 deletions cmd/clusterctl/pkg/client/config/providers_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

const (
ClusterAPIName = "cluster-api"
KubeadmBootstrapProviderName = "kubeadm"
KubeadmBootstrapProviderName = "kubeadm-bootstrap"
ProvidersConfigKey = "providers"
)

Expand Down Expand Up @@ -70,11 +70,11 @@ func (p *providersClient) defaults() []Provider {
// cluster API core provider
&provider{
name: ClusterAPIName,
url: "https://github.com/kubernetes-sigs/cluster-api/releases/latest/cluster-api-components.yaml",
url: "https://github.com/kubernetes-sigs/cluster-api/releases/latest/core-components.yaml",
providerType: clusterctlv1.CoreProviderType,
},

// Infrastructure providersClient
// Infrastructure providers
&provider{
name: "aws",
url: "https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/latest/infrastructure-components.yaml",
Expand All @@ -91,11 +91,10 @@ func (p *providersClient) defaults() []Provider {
providerType: clusterctlv1.InfrastructureProviderType,
},

// Bootstrap providersClient
// TODO: CABPK in v1alpha3 will be included into CAPI, so this entry can be removed as soon as v1alpha3 is ready for test
// Bootstrap providers
&provider{
name: KubeadmBootstrapProviderName,
url: "https://github.com/kubernetes-sigs/cluster-api-bootstrap-provider-kubeadm/releases/latest/bootstrap-components.yaml",
url: "https://github.com/kubernetes-sigs/cluster-api/releases/latest/bootstrap-components.yaml",
providerType: clusterctlv1.BootstrapProviderType,
},
}
Expand Down