| # 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_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): |
| # 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')) |
| 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) |
| |
| region = get_region_for_cluster(api, goma_config_dir, cluster) |
| api.gcloud( |
| 'container', |
| 'clusters', |
| 'get-credentials', |
| '--project=%s' % project, |
| '--region=%s' % region, |
| cluster, |
| step_name='get credential for %s' % project) |
| |
| with api.context(env={ |
| 'CLOUDSDK_COMPUTE_REGION': region, |
| 'CLOUDSDK_CONTAINER_CLUSTER': cluster, |
| }): |
| api.kubectl( |
| 'apply', |
| '-f', |
| str(goma_config_dir.join('k8s', cluster, 'goma')), |
| step_name='deploy-goma', |
| ) |
| api.kubectl( |
| "annotate", |
| "serviceaccount", |
| "--overwrite", |
| "default", |
| "iam.gke.io/gcp-service-account=rbe-cluster@%s.iam.gserviceaccount.com" |
| % project, |
| step_name="annotate default ksa") |
| 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, 'ingress')), |
| step_name='deploy-ingress', |
| ) |
| deploy_nginx_ssl(api, project, cluster) |
| 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'})) |
| |
| 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 |
| yield api.test( |
| 'deploy ssl' |
| ) + default_properties + region_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 + 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 + api.override_step_data( |
| 'get addr', api.raw_io.stream_output('test')) + api.buildbucket.try_build( |
| git_repo='https://fuchsia.googlesource.com/integration') |