File: //snap/google-cloud-cli/396/lib/googlecloudsdk/command_lib/code/local_files.py
# -*- coding: utf-8 -*- #
# Copyright 2019 Google LLC. All Rights Reserved.
#
# 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.
"""Library for generating the files for local development environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import functools
import itertools
from googlecloudsdk.command_lib.code import builders
from googlecloudsdk.command_lib.code import local
from googlecloudsdk.command_lib.code import yaml_helper
from googlecloudsdk.core import yaml
import six
_SKAFFOLD_TEMPLATE = """
apiVersion: skaffold/v2beta5
kind: Config
build:
artifacts: []
deploy:
kubectl:
manifests: []
"""
class LocalRuntimeFiles(object):
"""Generates the developement environment files for a project."""
def __init__(self, settings):
"""Initialize LocalRuntimeFiles.
Args:
settings: Local development settings.
"""
self._settings = settings
def KubernetesConfig(self):
"""Create a kubernetes config file.
Returns:
Text of a kubernetes config file.
"""
if self._settings.cpu:
if isinstance(self._settings.cpu, six.text_type):
if not self._settings.cpu.endswith('m'):
raise ValueError('cpu limit must be defined as an integer or as '
'millicpus')
user_cpu = int(self._settings.cpu[:-1]) / 1000.0
else:
user_cpu = self._settings.cpu
cpu_request = min(0.1, user_cpu)
else:
cpu_request = None
code_generators = [
local.AppContainerGenerator(
self._settings.service_name, self._settings.image,
self._settings.env_vars, self._settings.env_vars_secrets,
self._settings.memory, self._settings.cpu, cpu_request,
self._settings.readiness_probe),
local.SecretsGenerator(self._settings.service_name,
self._settings.env_vars_secrets,
self._settings.volumes_secrets,
self._settings.namespace,
self._settings.allow_secret_manager)
]
credential_generator = None
if isinstance(self._settings.credential, local.ServiceAccountSetting):
credential_generator = local.CredentialGenerator(
functools.partial(local.GetServiceAccountSecret,
self._settings.credential.name))
code_generators.append(credential_generator)
elif isinstance(self._settings.credential,
local.ApplicationDefaultCredentialSetting):
credential_generator = local.CredentialGenerator(local.GetUserCredential)
code_generators.append(credential_generator)
if self._settings.cloudsql_instances:
if not credential_generator:
raise ValueError('A credential generator must be defined when cloudsql '
'instances are defined.')
cloudsql_proxy = local.CloudSqlProxyGenerator(
self._settings.cloudsql_instances, credential_generator.GetInfo())
code_generators.append(cloudsql_proxy)
return _GenerateKubeConfigs(code_generators)
def SkaffoldConfig(self, kubernetes_file_path):
"""Create a skaffold yaml file.
Args:
kubernetes_file_path: Path to the kubernetes config file.
Returns:
Text of the skaffold yaml file.
"""
skaffold_yaml = yaml.load(_SKAFFOLD_TEMPLATE)
manifests = yaml_helper.GetOrCreate(
skaffold_yaml, ('deploy', 'kubectl', 'manifests'), constructor=list)
manifests.append(kubernetes_file_path)
artifact = {'image': self._settings.image}
# Need to escape file paths for the yaml encoder. The yaml encoder will
# interpret \ as the beginning of an escape character. Windows paths may
# have backslashes.
artifact['context'] = six.ensure_text(
self._settings.context.encode('unicode_escape'))
if isinstance(self._settings.builder, builders.BuildpackBuilder):
artifact['buildpacks'] = {
'builder': self._settings.builder.builder,
}
if self._settings.builder.devmode:
artifact['buildpacks']['env'] = ['GOOGLE_DEVMODE=1']
artifact['sync'] = {'auto': {}}
if self._settings.builder.trust:
artifact['buildpacks']['trustBuilder'] = True
else:
# Macos needs a relative path or else
# e2e.surface.code.dev_mac_test.MacE2ETest.testNamespace fails.
dockerfile_rel_path = self._settings.builder.DockerfileRelPath(
self._settings.context)
artifact['docker'] = {
'dockerfile':
six.ensure_text(dockerfile_rel_path.encode('unicode_escape'))
}
artifacts = yaml_helper.GetOrCreate(
skaffold_yaml, ('build', 'artifacts'), constructor=list)
artifacts.append(artifact)
if self._settings.local_port:
port_forward_config = {
'resourceType': 'service',
'resourceName': self._settings.service_name,
'port': 8080,
'localPort': self._settings.local_port
}
if self._settings.namespace:
port_forward_config['namespace'] = self._settings.namespace
skaffold_yaml['portForward'] = [port_forward_config]
return yaml.dump(skaffold_yaml)
def _GenerateKubeConfigs(code_generators):
"""Generate Kubernetes yaml configs.
Args:
code_generators: Iterable of KubeConfigGenerator.
Returns:
Iterable of dictionaries representing kubernetes yaml configs.
"""
kube_configs = []
for code_generator in code_generators:
kube_configs.extend(code_generator.CreateConfigs())
deployments = [
config for config in kube_configs if config['kind'] == 'Deployment'
]
for deployment, code_generator in itertools.product(deployments,
code_generators):
code_generator.ModifyDeployment(deployment)
for deployment in deployments:
containers = yaml_helper.GetAll(deployment,
('spec', 'template', 'spec', 'containers'))
for container, code_generator in itertools.product(containers,
code_generators):
code_generator.ModifyContainer(container)
return yaml.dump_all(kube_configs)