blob: 82ce4d0a4757ecb7b464660982e3e4b1ec6ea00b [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Recipe for deploying goma configurations."""
from recipe_engine.recipe_api import Property
from string import Template
DEPS = [
'fuchsia/docker',
'fuchsia/gcloud',
'fuchsia/git',
'fuchsia/goma',
'fuchsia/gsutil',
'fuchsia/kubectl',
'fuchsia/yaml',
'recipe_engine/archive',
'recipe_engine/buildbucket',
'recipe_engine/cipd',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/step',
'recipe_engine/time',
'recipe_engine/url',
]
PROPERTIES = {
'repository':
Property(
kind=str,
help='repository that hold the goma configurations',
default='https://fuchsia.googlesource.com/infra/config'),
'config_root':
Property(
kind=str,
help='root directory in repository that stores goma configurations',
default='goma'),
'domain':
Property(kind=str, help='domain', default='gcr.io'),
'project':
Property(
kind=str,
help='gcloud project that hosting goma clusters',
default='goma-fuchsia'),
'cluster':
Property(kind=str, help='the name of the cluster', default='rbe-dev'),
'toolchain_project':
Property(
kind=str,
help='gcloud project that hosting goma clusters',
default='fuchsia-toolchain-images-gcr'),
'gke_operation':
Property(
kind=str,
help='operation argument for gcloud deployment manager, can be \'create\' or \'update\'. Leave it blank means skip gke related steps',
default=''),
'deploy_endpoint':
Property(
kind=bool, help='deploy endpoint configuration.', default=False),
'reload_services':
Property(
kind=bool, help='deploy endpoint configuration.', default=True),
}
YAML_TEMPLATE_TEST_DATA = """
type: google.api.Service
config_version: 3
name: $CLUSTER.endpoints.$PROJECT_ID.cloud.goog
title: Goma gRPC API on $CLUSTER
apis:
- name: devtools_goma.ExecService
- name: devtools_goma.FileService
- name: devtools_goma.LogService
documentation:
summary: >-
Goma gRPC API on $CLUSTER in $PROJECT_ID project.
endpoints:
- name: $CLUSTER.endpoints.$PROJECT_ID.cloud.goog
target: "$ADDR"
"""
GOMATOOLS_VERSION = 'git_revision:22684ee9174606073a2604e1d58fbf03ba3ad564'
GOMA_SERVICES = [
'auth-server',
'exec-server',
'execlog-server',
'file-server',
'frontend',
]
def get_zone_for_cluster(api, goma_config_dir, cluster):
cluster_yaml = goma_config_dir.join('gke', cluster, 'cluster.yaml')
if not api.path.exists(cluster_yaml):
raise api.step.StepFailure('unknown cluster name %s' %
cluster) # pragma no cover
return api.yaml.retrieve_field(cluster_yaml, 'zone')
def get_region_for_cluster(api, goma_config_dir, cluster):
storage_yaml = goma_config_dir.join('gke-res', cluster, 'storage.yaml')
if not api.path.exists(storage_yaml):
raise api.step.StepFailure('unknown cluster name %s' %
cluster) # pragma no cover
return api.yaml.retrieve_field(storage_yaml, 'region')
def generate_yaml_from_template(api,
input_yaml,
output_yaml,
project,
cluster,
addr,
test_data=''):
replace_dict = {
'PROJECT_ID': project,
'CLUSTER': cluster,
'ADDR': addr,
}
infile = api.file.read_text(
'read input template %s' % input_yaml, input_yaml, test_data=test_data)
outfile = ''
for curline in infile.splitlines(True):
curline_temp = Template(curline)
curline = curline_temp.substitute(replace_dict)
outfile += curline
api.file.write_text('write yaml %s' % output_yaml, output_yaml, outfile)
def ensure_gomatools(api, gomatools_dir):
with api.step.nest('ensure gomatools'):
pkgs = api.cipd.EnsureFile()
pkgs.add_package(
'fuchsia_internal/third_party/goma/server/gomatools/linux-amd64',
GOMATOOLS_VERSION)
api.cipd.ensure(gomatools_dir, pkgs)
def deploy_nginx_ssl(api, project, cluster, zone, region):
# Check if SSL certs already exist.
kube_result = api.kubectl(
'get',
'secret',
'nginx-ssl',
step_name='kubectl get ngnix secret',
ok_ret='any',
)
if kube_result.retcode == 0:
return
# Generate new SSL certs.
gomatools_dir = api.path['cleanup'].join('goma')
ensure_gomatools(api, gomatools_dir)
with api.context(cwd=gomatools_dir):
api.step('gen nginx certs', [
gomatools_dir.join('tlscert'), '-ip',
'{}.endpoints.{}.cloud.goog'.format(cluster, project)
])
api.file.move('rename key.pem', gomatools_dir.join('key.pem'),
gomatools_dir.join('nginx.key'))
api.file.move('rename cert.pem', gomatools_dir.join('cert.pem'),
gomatools_dir.join('nginx.crt'))
with api.context(
env={
'CLOUDSDK_COMPUTE_REGION': region,
'CLOUDSDK_COMPUTE_ZONE': zone,
'CLOUDSDK_CONTAINER_CLUSTER': cluster,
}):
api.kubectl('create', 'secret', 'generic', 'nginx-ssl',
'--from-file=%s' % str(gomatools_dir.join('nginx.crt')),
'--from-file=%s' % str(gomatools_dir.join('nginx.key')))
def RunSteps(api, repository, config_root, domain, project, cluster,
toolchain_project, gke_operation, deploy_endpoint,
reload_services):
api.gcloud(
'config', 'set', 'project', project, step_name='set gcloud project')
# checkout
infra_config_dir = api.path['start_dir'].join('config')
goma_config_dir = infra_config_dir.join(config_root)
api.git.checkout(
url=repository, path=infra_config_dir, submodules=False, cache=False)
# for recipe tests, add mock files.
api.path.mock_add_paths(
goma_config_dir.join('gke', 'rbe-dev', 'cluster.yaml'))
api.path.mock_add_paths(
goma_config_dir.join('gke-res', 'rbe-dev', 'storage.yaml'))
if gke_operation:
gke_res_dir = goma_config_dir.join('gke-res', cluster)
gke_dir = goma_config_dir.join('gke', cluster)
api.gcloud(
'deployment-manager',
'deployments',
gke_operation,
'%s-global-addr' % cluster,
'--config',
gke_res_dir.join('global-addr.yaml'),
step_name='deploy-global-addr')
api.gcloud(
'deployment-manager',
'deployments',
gke_operation,
'%s-storage' % cluster,
'--config',
gke_res_dir.join('storage.yaml'),
step_name='deploy-storage')
api.gcloud(
'deployment-manager',
'deployments',
gke_operation,
cluster,
'--config',
gke_dir.join('cluster.yaml'),
step_name='deploy-cluster')
if deploy_endpoint:
endpoints_dir = goma_config_dir.join('endpoints')
ip_name = '%s-endpoints-address' % cluster
addr = api.gcloud(
'compute',
'addresses',
'describe',
ip_name,
'--global',
'--format=get(address)',
step_name='get addr',
stdout=api.raw_io.output()).stdout.strip()
if not addr:
raise api.step.InfraFailure('address not found in %s' %
ip_name) # pragma no cover
api_config_yaml = api.path['cleanup'].join('api_config.yaml')
service_pb = api.path['cleanup'].join('service_descriptor.pb')
generate_yaml_from_template(
api,
endpoints_dir.join('api_config.yaml.in'),
api_config_yaml,
project,
cluster,
addr,
test_data=YAML_TEMPLATE_TEST_DATA)
with api.docker.create(
api.url.join(domain, toolchain_project, 'frontend') + ':' +
'latest') as temp_copy:
api.docker.copy(temp_copy, ['/opt/goma/etc/service_descriptor.pb'],
api.path['cleanup'])
api.gcloud('endpoints', 'services', 'deploy', api_config_yaml, service_pb)
zone = get_zone_for_cluster(api, goma_config_dir, cluster)
api.gcloud(
'container',
'clusters',
'get-credentials',
'--project=%s' % project,
'--zone=%s' % zone,
cluster,
step_name='get credential for %s' % project)
region = get_region_for_cluster(api, goma_config_dir, cluster)
deploy_nginx_ssl(api, project, cluster, zone, region)
with api.context(
env={
'CLOUDSDK_COMPUTE_REGION': region,
'CLOUDSDK_COMPUTE_ZONE': zone,
'CLOUDSDK_CONTAINER_CLUSTER': cluster,
}):
api.kubectl(
'apply',
'-f',
str(goma_config_dir.join('k8s', cluster, 'backend-config')),
step_name='deploy-backend-config',
)
api.kubectl(
'apply',
'-f',
str(goma_config_dir.join('k8s', cluster, 'goma')),
step_name='deploy-goma',
)
api.kubectl(
'apply',
'-f',
str(goma_config_dir.join('k8s', cluster, 'ingress')),
step_name='deploy-ingress',
)
if reload_services:
for service in GOMA_SERVICES:
api.kubectl(
'rollout',
'restart',
'deployment',
service,
step_name='reload %s' % service)
def GenTests(api):
default_properties = api.properties(
repository='https://fuchsia.googlesource.com/infra/config',
config_root='goma',
domain='gcr.io',
project='goma-fuchsia',
cluster='rbe-dev',
gke_operation='create',
deploy_endpoint=True,
rbe_service_account='rbe-cluster',
force_update_secrets=False)
force_update_properties = api.properties(
repository='https://fuchsia.googlesource.com/infra/config',
config_root='goma',
domain='gcr.io',
project='goma-fuchsia',
cluster='rbe-dev',
gke_operation='create',
deploy_endpoint=True,
rbe_service_account='rbe-cluster',
force_update_secrets=True)
default_service_account_properties = api.properties(
repository='https://fuchsia.googlesource.com/infra/config',
config_root='goma',
domain='gcr.io',
project='goma-fuchsia',
cluster='rbe-dev',
gke_operation='create',
deploy_endpoint=True,
rbe_service_account='',
force_update_secrets=True)
region_step_data = api.step_data(
'load yaml [START_DIR]/config/goma/gke-res/rbe-dev/storage.yaml',
stdout=api.json.output({'region': 'us-central'}))
zone_step_data = api.step_data(
'load yaml [START_DIR]/config/goma/gke/rbe-dev/cluster.yaml',
stdout=api.json.output({'zone': 'us-central1-a'}))
yield api.test('default') + default_properties + api.override_step_data(
'get addr', api.raw_io.stream_output('test')) + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration'
) + region_step_data + zone_step_data
yield api.test(
'deploy ssl'
) + default_properties + region_step_data + zone_step_data + api.override_step_data(
'get addr', api.raw_io.stream_output('test')) + api.override_step_data(
'kubectl get ngnix secret', retcode=1) + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')
yield api.test(
'force update'
) + force_update_properties + region_step_data + zone_step_data + api.override_step_data(
'get addr', api.raw_io.stream_output('test')) + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')
yield api.test(
'default service account'
) + default_service_account_properties + region_step_data + zone_step_data + api.override_step_data(
'get addr', api.raw_io.stream_output('test')) + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')