File: //snap/google-cloud-cli/394/lib/surface/app/instances/scp.py
# -*- coding: utf-8 -*- #
# Copyright 2017 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.
"""The `app instances ssh` command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.app import appengine_api_client
from googlecloudsdk.api_lib.compute import base_classes as compute_base_classes
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.app import exceptions as command_exceptions
from googlecloudsdk.command_lib.app import flags
from googlecloudsdk.command_lib.app import iap_tunnel
from googlecloudsdk.command_lib.app import ssh_common
from googlecloudsdk.command_lib.util.ssh import ssh
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class ScpGa(base.Command):
"""SCP from or to the VM of an App Engine Flexible instance."""
detailed_help = {
'DESCRIPTION': textwrap.dedent("""\
*{command}* lets you remotely copy files to or from an App Engine
Flexible instance.""") + ssh_common.DETAILED_HELP,
'EXAMPLES': """\
To copy one file from a remote instance to the local machine, run:
$ {command} --service=s1 --version=v1 i1:remote_file local_file
To copy several local files to a remote instance, run:
$ {command} --service=s1 --version=v1 local_1 local_1 i1:remote_dir
To use recursive copy, run:
$ {command} --service=s1 --version=v1 --recurse local_dir i1:remote_dir
""",
}
@staticmethod
def Args(parser):
flags.AddServiceVersionSelectArgs(parser)
iap_tunnel.AddSshTunnelArgs(parser)
parser.add_argument(
'--recurse',
action='store_true',
help='Upload directories recursively.')
parser.add_argument(
'--compress',
action='store_true',
help='Enable compression.')
parser.add_argument(
'sources',
help='Specifies the files to copy.',
metavar='[INSTANCE:]SRC',
nargs='+')
parser.add_argument(
'destination',
help='Specifies a destination for the source files.',
metavar='[INSTANCE:]DEST')
def Run(self, args):
"""Securily copy files from/to a running flex instance.
Args:
args: argparse.Namespace, the args the command was invoked with.
Raises:
InvalidInstanceTypeError: The instance is not supported for SSH.
MissingVersionError: The version specified does not exist.
MissingInstanceError: The instance specified does not exist.
UnattendedPromptError: Not running in a tty.
OperationCancelledError: User cancelled the operation.
ssh.CommandError: The SCP command exited with SCP exit code, which
usually implies that a connection problem occurred.
Returns:
int, The exit code of the SCP command.
"""
api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack())
env = ssh.Environment.Current()
env.RequireSSH()
keys = ssh.Keys.FromFilename()
keys.EnsureKeysExist(overwrite=False)
# Make sure we have a unique remote
dst = ssh.FileReference.FromPath(args.destination)
srcs = [ssh.FileReference.FromPath(source) for source in args.sources]
ssh.SCPCommand.Verify(srcs, dst, single_remote=True)
remote = dst.remote or srcs[0].remote
instance = remote.host
if not dst.remote: # Make sure all remotes point to the same ref
for src in srcs:
src.remote = remote
try:
version_resource = api_client.GetVersionResource(args.service,
args.version)
except apitools_exceptions.HttpNotFoundError:
raise command_exceptions.MissingVersionError('{}/{}'.format(
args.service, args.version))
project = properties.VALUES.core.project.GetOrFail()
res = resources.REGISTRY.Parse(
instance,
params={
'appsId': project,
'versionsId': args.version,
'instancesId': instance,
'servicesId': args.service,
},
collection='appengine.apps.services.versions.instances')
instance_name = res.RelativeName()
try:
instance_resource = api_client.GetInstanceResource(res)
except apitools_exceptions.HttpNotFoundError:
raise command_exceptions.MissingInstanceError(instance_name)
iap_tunnel_args = iap_tunnel.CreateSshTunnelArgs(
args=args,
api_client=api_client,
track=self.ReleaseTrack(),
project=project,
version=version_resource,
instance=instance_resource)
region = '-'.join(instance_resource.vmZoneName.split('-')[:-1])
user = ssh.GetDefaultSshUsername()
project = ssh_common.GetComputeProject(self.ReleaseTrack())
oslogin_state = ssh.GetOsloginState(
None,
project,
user,
keys.GetPublicKey().ToEntry(),
None,
self.ReleaseTrack(),
app_engine_params={
'appsId': project.name,
'servicesId': res.servicesId,
'versionsId': res.versionsId,
'instancesId': instance,
'serviceAccount': (
version_resource.serviceAccount
if version_resource.serviceAccount
else ''
),
'region': region
},
messages=compute_base_classes.ComputeApiHolder(
self.ReleaseTrack()
).client.messages,
)
cert_file = None
if oslogin_state.third_party_user or oslogin_state.require_certificates:
cert_file = ssh.CertFileFromAppEngineInstance(
project.name, res.servicesId, res.versionsId, instance
)
connection_details = ssh_common.PopulatePublicKey(
api_client, args.service, args.version, remote.host,
keys.GetPublicKey(), oslogin_state.user, oslogin_state.oslogin_enabled)
# Update all remote references
remote.host = connection_details.remote.host
remote.user = connection_details.remote.user
return ssh.SCPCommand(
srcs,
dst,
identity_file=keys.key_file,
cert_file=cert_file,
compress=args.compress,
recursive=args.recurse,
options=connection_details.options,
iap_tunnel_args=iap_tunnel_args).Run(env)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class ScpBeta(base.Command):
"""SCP from or to the VM of an App Engine Flexible environment instance."""
detailed_help = {
'DESCRIPTION': textwrap.dedent("""\
*{command}* lets you remotely copy files to or from an App Engine
Flexible environment instance.""") + ssh_common.DETAILED_HELP,
'EXAMPLES': """\
To copy one file from a remote instance to the local machine, run:
$ {command} --service=s1 --version=v1 i1:remote_file local_file
To copy several local files to a remote instance, run:
$ {command} --service=s1 --version=v1 local_1 local_1 i1:remote_dir
To use recursive copy, run:
$ {command} --service=s1 --version=v1 --recurse local_dir i1:remote_dir
""",
}
@staticmethod
def Args(parser):
flags.AddServiceVersionSelectArgs(parser)
iap_tunnel.AddSshTunnelArgs(parser)
parser.add_argument(
'--recurse',
action='store_true',
help='Upload directories recursively.')
parser.add_argument(
'--compress',
action='store_true',
help='Enable compression.')
parser.add_argument(
'sources',
help='Specifies the files to copy.',
metavar='[INSTANCE:]SRC',
nargs='+')
parser.add_argument(
'destination',
help='Specifies a destination for the source files.',
metavar='[INSTANCE:]DEST')
def Run(self, args):
"""Securily copy files from/to a running flex instance.
Args:
args: argparse.Namespace, the args the command was invoked with.
Raises:
InvalidInstanceTypeError: The instance is not supported for SSH.
MissingVersionError: The version specified does not exist.
MissingInstanceError: The instance specified does not exist.
UnattendedPromptError: Not running in a tty.
OperationCancelledError: User cancelled the operation.
ssh.CommandError: The SCP command exited with SCP exit code, which
usually implies that a connection problem occurred.
Returns:
int, The exit code of the SCP command.
"""
api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack())
env = ssh.Environment.Current()
env.RequireSSH()
keys = ssh.Keys.FromFilename()
keys.EnsureKeysExist(overwrite=False)
# Make sure we have a unique remote
dst = ssh.FileReference.FromPath(args.destination)
srcs = [ssh.FileReference.FromPath(source) for source in args.sources]
ssh.SCPCommand.Verify(srcs, dst, single_remote=True)
remote = dst.remote or srcs[0].remote
instance = remote.host
if not dst.remote: # Make sure all remotes point to the same ref
for src in srcs:
src.remote = remote
try:
version_resource = api_client.GetVersionResource(args.service,
args.version)
except apitools_exceptions.HttpNotFoundError:
raise command_exceptions.MissingVersionError('{}/{}'.format(
args.service, args.version))
project = properties.VALUES.core.project.GetOrFail()
res = resources.REGISTRY.Parse(
instance,
params={
'appsId': project,
'versionsId': args.version,
'instancesId': instance,
'servicesId': args.service,
},
collection='appengine.apps.services.versions.instances')
instance_name = res.RelativeName()
try:
instance_resource = api_client.GetInstanceResource(res)
except apitools_exceptions.HttpNotFoundError:
raise command_exceptions.MissingInstanceError(instance_name)
iap_tunnel_args = iap_tunnel.CreateSshTunnelArgs(args, api_client,
self.ReleaseTrack(),
project, version_resource,
instance_resource)
region = '-'.join(instance_resource.vmZoneName.split('-')[:-1])
user = ssh.GetDefaultSshUsername()
project = ssh_common.GetComputeProject(self.ReleaseTrack())
oslogin_state = ssh.GetOsloginState(
None,
project,
user,
keys.GetPublicKey().ToEntry(),
None,
self.ReleaseTrack(),
app_engine_params={
'appsId': project.name,
'servicesId': res.servicesId,
'versionsId': res.versionsId,
'instancesId': instance,
'serviceAccount': (
version_resource.serviceAccount
if version_resource.serviceAccount
else ''
),
'region': region
},
messages=compute_base_classes.ComputeApiHolder(
self.ReleaseTrack()
).client.messages,
)
cert_file = None
if oslogin_state.third_party_user or oslogin_state.require_certificates:
cert_file = ssh.CertFileFromAppEngineInstance(
project.name, res.servicesId, res.versionsId, instance
)
connection_details = ssh_common.PopulatePublicKey(
api_client, args.service, args.version, remote.host,
keys.GetPublicKey(), oslogin_state.user, oslogin_state.oslogin_enabled)
# Update all remote references
remote.host = connection_details.remote.host
remote.user = connection_details.remote.user
cmd = ssh.SCPCommand(
srcs,
dst,
identity_file=keys.key_file,
cert_file=cert_file,
compress=args.compress,
recursive=args.recurse,
options=connection_details.options,
iap_tunnel_args=iap_tunnel_args)
return cmd.Run(env)