def create(self): component = Component( name=self.slug, repo=None, version='master', repo_url=f"[email protected]:{self.github_owner}/component-{self.slug}.git", ) if component.target_directory.exists(): raise click.ClickException( f"Unable to add component {self.name}: {component.target_directory} already exists.") click.secho(f"Adding component {self.name}...", bold=True) component_template = __install_dir__ / 'component-template' cookiecutter(str(component_template.resolve()), no_input=True, output_dir='dependencies', extra_context=self.cookiecutter_args()) repo = git.create_repository(component.target_directory) component = component._replace(repo=repo) git.add_remote(repo, 'origin', component.repo_url) index = repo.index index.add('*') index.add('.github') index.add('.*.yml') git.commit(repo, 'Initial commit', self.config) click.echo(' > Installing component') create_component_symlinks(self.config, component) targetfile = P('inventory', 'targets', 'cluster.yml') target = yaml_load(targetfile) target['classes'].append(f"components.{self.slug}") target['classes'].insert(0, f"defaults.{self.slug}") yaml_dump(target, targetfile) click.secho(f"Component {self.name} successfully added 🎉", bold=True)
def create(self): component = Component( name=self.slug, repo=None, version="master", repo_url= f"[email protected]:{self.github_owner}/component-{self.slug}.git", ) if component.target_directory.exists(): raise click.ClickException( f"Unable to add component {self.name}: {component.target_directory} already exists." ) click.secho(f"Adding component {self.name}...", bold=True) component_template = __install_dir__ / "component-template" cookiecutter( str(component_template.resolve()), no_input=True, output_dir="dependencies", extra_context=self.cookiecutter_args(), ) repo = git.create_repository(component.target_directory) component = component._replace(repo=repo) git.add_remote(repo, "origin", component.repo_url) index = repo.index index.add("*") index.add(".github") index.add(".gitignore") index.add(".*.yml") index.add(".editorconfig") git.commit(repo, "Initial commit", self.config) click.echo(" > Installing component") try: create_component_symlinks(self.config, component) targetfile = P("inventory", "targets", "cluster.yml") insert_into_inventory_targets_cluster(targetfile, self.slug) insert_into_jsonnetfile(P("jsonnetfile.json"), component.target_directory) # call fetch_jsonnet_libraries after updating jsonnetfile to # symlink new component into vendor/ fetch_jsonnet_libraries() except FileNotFoundError: # TODO: This should maybe cleanup the "dependencies" subdirectory # (since we just created it). click.echo("Cannot find catalog files. Did you forget to run " "'catalog compile' in the current directory?") else: click.secho(f"Component {self.name} successfully added 🎉", bold=True)
def delete(self): component = Component( name=self.slug, repo=None, repo_url="", ) if component.target_directory.exists(): if not self.config.force: click.confirm( "Are you sure you want to delete component " f"{self.slug}? This action cannot be undone", abort=True, ) delete_component_symlinks(self.config, component) rmtree(component.target_directory) targetfile = P("inventory", "targets", "cluster.yml") remove_from_inventory_targets_cluster(targetfile, self.slug) remove_from_jsonnetfile(P("jsonnetfile.json"), component.target_directory) # Fetch jsonnet libs after removing component from jsonnetfile to # remove symlink to removed component in vendor/ fetch_jsonnet_libraries() click.secho(f"Component {self.slug} successfully deleted 🎉", bold=True) else: raise click.BadParameter("Cannot find component with slug " f"'{self.slug}'.")
def test_create_component_symlinks_fails(data: Config, tmp_path: Path): os.chdir(tmp_path) component = Component( name='my-component', repo=None, version='master', repo_url=None, ) with pytest.raises(click.ClickException) as excinfo: dependency_mgmt.create_component_symlinks(data, component) assert component.name in str(excinfo)
def test_create_legacy_component_symlinks(capsys, data: Config, tmp_path): os.chdir(tmp_path) component = Component( name='my-component', repo=None, version='master', repo_url=None, ) target_dir = Path('inventory/classes/components') target_dir.mkdir(parents=True, exist_ok=True) dependency_mgmt.create_component_symlinks(data, component) capture = capsys.readouterr() assert (target_dir / f"{component.name}.yml").is_symlink() assert 'Old-style component detected.' in capture.out
def _setup(tmp_path, filter): os.chdir(tmp_path) test_run_component_new_command(tmp_path=tmp_path) target = "target" targetdir = tmp_path / "compiled" / target / "test" os.makedirs(targetdir, exist_ok=True) testf = targetdir / "object.yaml" with open(testf, "w") as objf: obj = { "metadata": { "name": "test", "namespace": "untouched", }, "kind": "Secret", "apiVersion": "v1", "stringData": { "content": "verysecret", }, } yaml.dump(obj, objf) with open( tmp_path / "dependencies" / "test-component" / "postprocess" / "filters.yml", "w", ) as filterf: yaml.dump(filter, filterf) config = Config() component = Component( "test-component", Repo(tmp_path / "dependencies" / "test-component"), "https://fake.repo.url", "master", ) inventory = { "classes": { "defaults.test-component", "global.common", "components.test-component", }, "parameters": { "test_component": { "namespace": "syn-test-component", }, }, } return testf, config, inventory, target, {"test-component": component}
def _setup(tmp_path, filter): os.chdir(tmp_path) test_run_component_new_command(tmp_path=tmp_path) target = 'target' targetdir = tmp_path / 'compiled' / target / 'test' os.makedirs(targetdir, exist_ok=True) testf = targetdir / 'object.yaml' with open(testf, 'w') as objf: obj = { 'metadata': { 'name': 'test', 'namespace': 'untouched', }, 'kind': 'Secret', 'apiVersion': 'v1', 'stringData': { 'content': 'verysecret', }, } yaml.dump(obj, objf) with open( tmp_path / 'dependencies' / 'test-component' / 'postprocess' / 'filters.yml', 'w') as filterf: yaml.dump(filter, filterf) config = Config() component = Component('test-component', Repo(tmp_path / 'dependencies' / 'test-component'), 'https://fake.repo.url', 'master') inventory = { 'classes': { 'defaults.test-component', 'global.common', 'components.test-component', }, 'parameters': { 'test_component': { 'namespace': 'syn-test-component', }, }, } return testf, config, inventory, target, {'test-component': component}
def test_create_component_symlinks(capsys, data: Config, tmp_path): os.chdir(tmp_path) component = Component( name='my-component', repo=None, version='master', repo_url=None, ) class_dir = Path('dependencies') / component.name / 'class' class_dir.mkdir(parents=True, exist_ok=True) (class_dir / f"{component.name}.yml").touch() (class_dir / 'defaults.yml').touch() target_dir = Path('inventory/classes/components') target_dir.mkdir(parents=True, exist_ok=True) target_defaults = Path('inventory/classes/defaults') target_defaults.mkdir(parents=True, exist_ok=True) dependency_mgmt.create_component_symlinks(data, component) capture = capsys.readouterr() assert (target_dir / f"{component.name}.yml").is_symlink() assert (target_defaults / f"{component.name}.yml").is_symlink() assert capture.out == ''
def test_fetch_components(patch_discover, patch_urls, patch_clone, data: Config, tmp_path): os.chdir(tmp_path) components = ['component-one', 'component-two'] # Prepare minimum component directories for component in components: class_dir = Path('dependencies') / component / 'class' class_dir.mkdir(parents=True, exist_ok=True) (class_dir / 'defaults.yml').touch(exist_ok=True) patch_discover.return_value = components patch_urls.return_value = [ Component( name=c, repo=None, repo_url='mock-url', version='master', ) for c in components] dependency_mgmt.fetch_components(data) print(data._components) for component in components: assert component in data._components assert (Path('inventory/classes/components') / f"{component}.yml").is_symlink() assert (Path('inventory/classes/defaults') / f"{component}.yml").is_symlink() assert data.get_component_repo(component) is not None
def compile_component(config: Config, component_path, value_files, search_paths, output_path): # Resolve all input to absolute paths to fix symlinks component_path = P(component_path).resolve() value_files = [P(f).resolve() for f in value_files] search_paths = [P(d).resolve() for d in search_paths] output_path = P(output_path).resolve() # Ignore 'component-' prefix in dir name component_name = component_path.stem.replace('component-', '') click.secho(f"Compile component {component_name}...", bold=True) temp_dir = P(tempfile.mkdtemp(prefix='component-')).resolve() original_working_dir = os.getcwd() os.chdir(temp_dir) try: if config.debug: click.echo(f" > Created temp workspace: {temp_dir}") _prepare_fake_inventory(temp_dir, component_name, component_path, value_files) # Create class for fake parameters with open(temp_dir / 'inventory/classes/fake.yml', 'w') as file: file.write(""" parameters: cloud: provider: cloudscale region: rma1 cluster: catalog_url: ssh://[email protected]/org/repo.git dist: test-distribution name: c-green-test-1234 customer: name: t-silent-test-1234 argocd: namespace: test kapitan: vars: target: test namespace: test """) # Create test target with open(temp_dir / 'inventory/targets/test.yml', 'w') as file: value_classes = "\n".join([f"- {c.stem}" for c in value_files]) file.write(f""" classes: - fake - defaults.{component_name} - components.{component_name} {value_classes} """) # Fake Argo CD lib (temp_dir / 'dependencies/lib').mkdir(exist_ok=True) with open(temp_dir / 'dependencies/lib/argocd.libjsonnet', 'w') as file: file.write(""" local ArgoApp(component, namespace, project='', secrets=true) = {}; local ArgoProject(name) = {}; { App: ArgoApp, Project: ArgoProject, } """) # Fetch Jsonnet libs fetch_jsonnet_libs(config, libs) # Compile component kapitan_compile(config, target='test', output_dir=output_path, search_paths=search_paths, fake_refs=True, reveal=True) click.echo(f" > Component compiled to {output_path / 'compiled/test'}") # prepare inventory and fake component object for postprocess inventory = inventory_reclass(temp_dir / 'inventory')['nodes']['test'] component = Component(component_name, Repo(component_path), 'https://fake.repo.url/', 'master') # We change the working directory to the output_path directory here, # as postprocess expects to find `compiled/<target>` in the working # directory. os.chdir(output_path) postprocess_components(config, inventory, 'test', {component_name: component}) finally: os.chdir(original_working_dir) if config.trace: click.echo(f" > Temp dir left in place {temp_dir}") else: if config.debug: click.echo(f" > Remove temp dir {temp_dir}") shutil.rmtree(temp_dir)