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

#444 implement multiple environments functionality #487

Merged
merged 2 commits into from
May 13, 2022
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: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ skaffold.yaml
.tox
.pytest_cache
.overrides
deployment.yaml
deployment.yaml
/deployment-configuration
zsinnema marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 22 additions & 2 deletions docs/build-deploy/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Different deployments often require different configurations.
For instance, we may want to assign less resources/replicas to a pod deployed locally
respect to the production build.

## How to set the environment

The environment of the current deployment can ve set with the parameter `--env` (`-e`)
of `harness-deployment`.

Expand All @@ -19,9 +21,7 @@ harness-deployment cloud-harness . -e dev
the following configuration files are potentially loaded (if they exist):

- `deployment-configuration/values-template-dev.yaml`
- `deployment-configuration/skaffold-template-dev.yaml`
- `deployment-configuration/codefresh-template-dev.yaml`
- `deployment-configuration/codefresh-build-template-dev.yaml`

And for each application:

Expand Down Expand Up @@ -58,3 +58,23 @@ b: 2
c: 3
d: 4
```

## Multiple environments

Multiple environments can be set by separating different environments with an hyphen, like `-e env1-env2`

For example, by running

```
harness-deployment cloud-harness . -e dev-test
```

We get the following loading order:

```
values.yaml
values-dev.yaml
values-test.yaml
```

The same principle applies to all templates.
184 changes: 106 additions & 78 deletions tools/cloudharness_utilities/codefresh.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from re import template
from .models import HarnessMainConfig
import oyaml as yaml
import yaml.representer
Expand Down Expand Up @@ -30,87 +31,104 @@ def literal_presenter(dumper, data):
yaml.add_representer(str, literal_presenter)


def create_codefresh_deployment_scripts(root_paths, env, include=(), exclude=(),
def create_codefresh_deployment_scripts(root_paths, envs=(), include=(), exclude=(),
template_name=CF_TEMPLATE_PATH, base_image_name=None,
values_manual_deploy: HarnessMainConfig=None, save=True):
values_manual_deploy: HarnessMainConfig = None, save=True):
"""
Entry point to create deployment scripts for codefresh: codefresh.yaml and helm chart
"""
template_name = f"codefresh-template-{env}.yaml"
out_filename = f"codefresh-{env}.yaml"
if include:
logging.info('Including the following subpaths to the build: %s.', ', '.join(include))

if exclude:
logging.info('Excluding the following subpaths to the build: %s.', ', '.join(exclude))
out_filename = f"codefresh-{'-'.join(envs)}.yaml"

codefresh = get_template(os.path.join(DEPLOYMENT_CONFIGURATION_PATH, template_name), True)
if include:
logging.info(
'Including the following subpaths to the build: %s.', ', '
.join(include)
)

if not codefresh:
if template_name != CF_TEMPLATE_PATH:
logging.warning("Template file %s not found. Codefresh script not created.", template_name)
if os.path.exists(os.path.join(HERE, CF_TEMPLATE_PATH)):
logging.info("Loading legacy template %s", CF_TEMPLATE_PATH)
codefresh = get_template(os.path.join(HERE, CF_TEMPLATE_PATH), True)
return
logging.info("Creating codefresh script %s", out_filename)
if exclude:
logging.info(
'Excluding the following subpaths to the build: %s.', ', '
.join(exclude)
)

if CF_BUILD_STEP_BASE in codefresh['steps']:
codefresh['steps'][CF_BUILD_STEP_BASE]['steps'] = {}
codefresh['steps'][CF_BUILD_STEP_STATIC]['steps'] = {}
codefresh['steps'][CF_BUILD_STEP_PARALLEL]['steps'] = {}
if CF_STEP_PUBLISH in codefresh['steps']:
codefresh['steps'][CF_STEP_PUBLISH]['steps'] = {}
codefresh = {}

for root_path in root_paths:
template_path = os.path.join(root_path, DEPLOYMENT_CONFIGURATION_PATH, template_name)
if os.path.exists(template_path):
tpl = get_template(template_path, True)
if CF_BUILD_STEP_BASE in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_BASE]
if CF_BUILD_STEP_STATIC in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_STATIC]
if CF_BUILD_STEP_PARALLEL in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_PARALLEL]
if CF_STEP_PUBLISH in codefresh['steps']:
del tpl['steps'][CF_STEP_PUBLISH]
codefresh = dict_merge(codefresh, tpl)

def codefresh_build_step_from_base_path(base_path, build_step, fixed_context=None, include=include):
for e in envs:
template_name = f"codefresh-template-{e}.yaml"
template_path = os.path.join(
root_path, DEPLOYMENT_CONFIGURATION_PATH, template_name)

for dockerfile_path in find_dockerfiles_paths(base_path):
app_relative_to_root = os.path.relpath(dockerfile_path, '.')
app_relative_to_base = os.path.relpath(dockerfile_path, base_path)
app_name = app_name_from_path(app_relative_to_base)
if include and not any(
f"/{inc}/" in dockerfile_path or dockerfile_path.endswith(f"/{inc}") for inc in include):
continue
if any(inc in dockerfile_path for inc in (list(exclude) + EXCLUDE_PATHS)):
continue
build = None
if CF_BUILD_STEP_BASE in codefresh['steps']:
build = codefresh_app_build_spec(
app_name=app_name,
app_context_path=os.path.relpath(fixed_context, '.') if fixed_context else app_relative_to_root,
dockerfile_path=os.path.join(
os.path.relpath(dockerfile_path, root_path) if fixed_context else '',
"Dockerfile"),
base_name=base_image_name,
helm_values=values_manual_deploy
)
codefresh['steps'][build_step]['steps'][app_name] = build
if CF_STEP_PUBLISH in codefresh['steps']:
codefresh['steps'][CF_STEP_PUBLISH]['steps']['publish_' + app_name] = codefresh_app_publish_spec(
app_name=app_name,
build_tag=build and build['tag'],
base_name=base_image_name
)

codefresh_build_step_from_base_path(os.path.join(root_path, BASE_IMAGES_PATH), CF_BUILD_STEP_BASE,
fixed_context=os.path.relpath(root_path, os.getcwd()), include=values_manual_deploy[KEY_TASK_IMAGES].keys())
codefresh_build_step_from_base_path(os.path.join(root_path, STATIC_IMAGES_PATH), CF_BUILD_STEP_STATIC,
include=values_manual_deploy[KEY_TASK_IMAGES].keys())
codefresh_build_step_from_base_path(os.path.join(root_path, APPS_PATH), CF_BUILD_STEP_PARALLEL)
tpl = get_template(template_path, True)
if tpl:
logging.info("Codefresh template found: %s", template_path)
tpl = get_template(template_path, True)
if 'steps' in tpl:
if codefresh and CF_BUILD_STEP_BASE in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_BASE]
if codefresh and CF_BUILD_STEP_STATIC in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_STATIC]
if codefresh and CF_BUILD_STEP_PARALLEL in codefresh['steps']:
del tpl['steps'][CF_BUILD_STEP_PARALLEL]
if codefresh and CF_STEP_PUBLISH in codefresh['steps']:
del tpl['steps'][CF_STEP_PUBLISH]
codefresh = dict_merge(codefresh, tpl)

if not 'steps' in codefresh:
continue

def codefresh_build_step_from_base_path(base_path, build_step, fixed_context=None, include=include):

for dockerfile_path in find_dockerfiles_paths(base_path):
app_relative_to_root = os.path.relpath(
dockerfile_path, '.')
app_relative_to_base = os.path.relpath(
dockerfile_path, base_path)
app_name = app_name_from_path(app_relative_to_base)
if include and not any(
f"/{inc}/" in dockerfile_path or dockerfile_path.endswith(f"/{inc}") for inc in include):
continue
if any(inc in dockerfile_path for inc in (list(exclude) + EXCLUDE_PATHS)):
continue
build = None
if CF_BUILD_STEP_BASE in codefresh['steps']:
build = codefresh_app_build_spec(
app_name=app_name,
app_context_path=os.path.relpath(
fixed_context, '.') if fixed_context else app_relative_to_root,
dockerfile_path=os.path.join(
os.path.relpath(
dockerfile_path, root_path) if fixed_context else '',
"Dockerfile"),
base_name=base_image_name,
helm_values=values_manual_deploy
)

if not type(codefresh['steps'][build_step]['steps']) == dict:
codefresh['steps'][build_step]['steps'] = {}

codefresh['steps'][build_step]['steps'][app_name] = build
if CF_STEP_PUBLISH in codefresh['steps']:
if not type(codefresh['steps'][CF_STEP_PUBLISH]['steps']) == dict:
codefresh['steps'][CF_STEP_PUBLISH]['steps'] = {}
codefresh['steps'][CF_STEP_PUBLISH]['steps']['publish_' + app_name] = codefresh_app_publish_spec(
app_name=app_name,
build_tag=build and build['tag'],
base_name=base_image_name
)

codefresh_build_step_from_base_path(os.path.join(root_path, BASE_IMAGES_PATH), CF_BUILD_STEP_BASE,
fixed_context=os.path.relpath(root_path, os.getcwd()), include=values_manual_deploy[KEY_TASK_IMAGES].keys())
codefresh_build_step_from_base_path(os.path.join(root_path, STATIC_IMAGES_PATH), CF_BUILD_STEP_STATIC,
include=values_manual_deploy[KEY_TASK_IMAGES].keys())
codefresh_build_step_from_base_path(os.path.join(
root_path, APPS_PATH), CF_BUILD_STEP_PARALLEL)

if not codefresh:
logging.warning(
"No template file found. Codefresh script not created.")
return

# Remove useless steps
codefresh['steps'] = {k: step for k, step in codefresh['steps'].items() if
Expand All @@ -130,8 +148,13 @@ def codefresh_build_step_from_base_path(base_path, build_step, fixed_context=Non
environment.append(
"CUSTOM_apps_%s_harness_secrets_%s=${{%s}}" % (app_name, secret_name, secret_name.upper()))

cmds = codefresh['steps']['prepare_deployment']['commands']
for i in range(len(cmds)):
cmds[i] = cmds[i].replace("$ENV", "-".join(envs))

if save:
codefresh_abs_path = os.path.join(os.getcwd(), DEPLOYMENT_PATH, out_filename)
codefresh_abs_path = os.path.join(
os.getcwd(), DEPLOYMENT_PATH, out_filename)
codefresh_dir = os.path.dirname(codefresh_abs_path)
if not os.path.exists(codefresh_dir):
os.makedirs(codefresh_dir)
Expand All @@ -153,11 +176,13 @@ def codefresh_template_spec(template_path, **kwargs):


def codefresh_app_publish_spec(app_name, build_tag, base_name=None):
title = app_name.capitalize().replace('-', ' ').replace('/', ' ').replace('.', ' ').strip()
title = app_name.capitalize().replace(
'-', ' ').replace('/', ' ').replace('.', ' ').strip()

step_spec = codefresh_template_spec(
template_path=CF_TEMPLATE_PUBLISH_PATH,
candidate="${{REGISTRY}}/%s:%s" % (get_image_name(app_name, base_name), build_tag or '${{DEPLOYMENT_TAG}}'),
candidate="${{REGISTRY}}/%s:%s" % (get_image_name(
app_name, base_name), build_tag or '${{DEPLOYMENT_TAG}}'),
title=title,
)
if not build_tag:
Expand All @@ -170,9 +195,10 @@ def app_specific_tag_variable(app_name):
return "${{ %s }}_${{DEPLOYMENT_PUBLISH_TAG}}" % app_name.replace('-', '_').upper()


def codefresh_app_build_spec(app_name, app_context_path, dockerfile_path="Dockerfile", base_name=None, helm_values: HarnessMainConfig={}):
def codefresh_app_build_spec(app_name, app_context_path, dockerfile_path="Dockerfile", base_name=None, helm_values: HarnessMainConfig = {}):
logging.info('Generating build script for ' + app_name)
title = app_name.capitalize().replace('-', ' ').replace('/', ' ').replace('.', ' ').strip()
title = app_name.capitalize().replace(
'-', ' ').replace('/', ' ').replace('.', ' ').strip()
build = codefresh_template_spec(
template_path=CF_BUILD_PATH,
image_name=get_image_name(app_name, base_name),
Expand All @@ -182,11 +208,13 @@ def codefresh_app_build_spec(app_name, app_context_path, dockerfile_path="Docker

specific_build_template_path = os.path.join(app_context_path, 'build.yaml')
if os.path.exists(specific_build_template_path):
logging.info("Specific build template found: %s" % (specific_build_template_path))
logging.info("Specific build template found: %s" %
(specific_build_template_path))
with open(specific_build_template_path) as f:
build_specific = yaml.safe_load(f)

build_args = build_specific.pop('build_arguments') if 'build_arguments' in build_specific else []
build_args = build_specific.pop(
'build_arguments') if 'build_arguments' in build_specific else []

build['build_arguments'].append('REGISTRY=${{REGISTRY}}/%s/' % base_name)

Expand All @@ -200,4 +228,4 @@ def codefresh_app_build_spec(app_name, app_context_path, dockerfile_path="Docker
d in helm_values['task-images']]
build['build_arguments'].extend(dependencies)

return build
return build
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ steps:
working_directory: .
commands:
- bash cloud-harness/install.sh
- harness-deployment cloud-harness . -t ${{CF_BUILD_ID}} -d ${{DOMAIN}} -r ${{REGISTRY}} -rs ${{REGISTRY_SECRET}} -e dev
- harness-deployment cloud-harness . -t ${{CF_BUILD_ID}} -d ${{DOMAIN}} -r ${{REGISTRY}} -rs ${{REGISTRY_SECRET}} -e $ENV
prepare_deployment_view:
commands:
- 'helm template ./deployment/helm --debug -n ${{NAMESPACE}}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ steps:
working_directory: .
commands:
- bash cloud-harness/install.sh
- harness-deployment . cloud-harness -t ${{DEPLOYMENT_TAG}} -d ${{DOMAIN}} -r ${{REGISTRY}} -rs ${{REGISTRY_SECRET}} -e prod
- harness-deployment . cloud-harness -t ${{DEPLOYMENT_TAG}} -d ${{DOMAIN}} -r ${{REGISTRY}} -rs ${{REGISTRY_SECRET}} -e $ENV
zsinnema marked this conversation as resolved.
Show resolved Hide resolved
prepare_deployment_view:
commands:
- 'helm template ./deployment/helm --debug -n ${{NAMESPACE}}'
Expand Down
Loading