feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA,
base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Apigee(base.Group):
"""Manage Apigee resources.
Commands for managing Google Cloud Apigee resources.
"""
category = base.API_PLATFORM_AND_ECOSYSTEMS_CATEGORY
detailed_help = {
"DESCRIPTION": "Manage Apigee resources.",
"EXAMPLES":
"""
To list API proxies in the active Cloud Platform project, run:
$ {command} apis list
To deploy an API proxy named ``hello-world'' to the ``test''
environment, run:
$ {command} apis deploy --environment=test --api=hello-world
To get the status of that deployment, run:
$ {command} deployments describe --environment=test --api=hello-world
To undeploy that API proxy, run:
$ {command} apis undeploy --environment=test --api=hello-world
"""
}
def Filter(self, context, args):
# TODO(b/190525329): Determine if command group works with project number
base.RequireProjectID(args)
del context, args

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 apis command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Apis(base.Group):
"""Manage Apigee API proxies."""
detailed_help = {
"EXAMPLES": """
To list all the API proxies in the active Cloud Platform project, run:
$ {command} list
To get details about a specific API proxy in a specific Apigee
organization, run:
$ {command} describe PROXY_NAME --organization=ORG_NAME
To get a JSON object containing revision-level details about an API
proxy, run:
$ {command} describe PROXY_NAME --verbose --format=json
To deploy the most recent revision of an API proxy into the ``eval''
deployment environment, run:
$ {command} deploy --api=PROXY_NAME --environment=eval
To deploy the first revision of that API proxy into the ``release''
deployment environment, run:
$ {command} deploy 1 --api=PROXY_NAME --environment=release
To undeploy whatever revision of PROXY_NAME is currently deployed in
ENVIRONMENT, run:
$ {command} undeploy --api=PROXY_NAME --environment=ENVIRONMENT
""",
}

View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to deploy an Apigee API proxy to an environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
from googlecloudsdk.core import log
class Deploy(base.DescribeCommand):
"""Deploy an API proxy to an environment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` installs an API proxy revision in an Apigee runtime environment.
By default, the API proxy's base path must not already be in use by a deployed
proxy in the target environment. To allow Apigee to undeploy any conflicting
API proxy as part of the deployment, use the `--override` command.
Once a particular revision of an API proxy has been deployed, that revision
can no longer be modified. Any updates to the API proxy must be saved as a new
revision.
""",
"EXAMPLES":
"""\
To deploy the latest revision of the API proxy named ``demo'' to the ``test''
environment, given that the API proxy and environment's matching Cloud
Platform project has been set in gcloud settings, run:
$ {command} --environment=test --api=demo
To deploy revision 3 of that proxy, owned by an organization named ``my-org'',
run, and replace any conflicting deployment that might already exist, run:
$ {command} 3 --organization=my-org --environment=test --api=demo --override
To deploy that proxy and print the resulting deployment as a JSON object, run:
$ {command} 3 --organization=my-org --environment=test --api=demo --format=json
"""
}
@staticmethod
def Args(parser):
parser.add_argument(
"--override",
action="store_true",
help=("Force the deployment of the new revision, overriding any " +
"currently deployed revision that would conflict with it.\n\n" +
"If an existing API proxy revision is deployed, set this flag " +
"to ensure seamless deployment with zero downtime. In this " +
"case, the existing revision remains deployed until the new " +
"revision is fully deployed.\n\n" +
"If unset, `{command}` will fail unless all conflicting API " +
"proxies are first undeployed from the environment. To do " +
"this, run `{parent_command} undeploy` on the conflicting " +
"deployment."))
help_text = {
"api": ("API proxy to be deployed. To get a list of available API " +
"proxies, run `{{parent_command}} list`."),
"environment": ("Environment in which to deploy the API proxy. To " +
"get a list of available environments, run " +
"`{{grandparent_command}} environments list`."),
"organization": ("Apigee organization of the proxy and environment. " +
"If unspecified, the Cloud Platform project's "
"associated organization will be used."),
}
fallthroughs = [defaults.GCPProductOrganizationFallthrough(),
defaults.StaticFallthrough("revision", "latest")]
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.api.revision",
"API proxy revision to be deployed and environment in which to deploy "
"it. Revisions can either be a positive revision number, or the "
"special value ``latest'', which will deploy the latest revision of "
"the API proxy. If revision is unspecified, the default is ``latest''.",
fallthroughs=fallthroughs,
help_texts=help_text)
def Run(self, args):
"""Run the deploy command."""
identifiers = args.CONCEPTS.revision.Parse().AsDict()
if identifiers["revisionsId"] == "latest":
latest_revision = apigee.APIsClient.Describe(identifiers)["revision"][-1]
log.status.Print("Using current latest revision `%s`"%latest_revision)
identifiers["revisionsId"] = latest_revision
result = apigee.APIsClient.Deploy(identifiers, args.override)
return result

View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee API proxy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee API proxy."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` shows metadata about an API proxy and its revisions.""",
"EXAMPLES":
"""\
To describe an API proxy called ``proxy-name'' given that its matching Cloud
Platform project has been set in gcloud settings, run:
$ {command} proxy-name
To describe an API proxy called ``other-proxy-name'' in another project whose
Apigee organization is named ``org-name'', run:
$ {command} other-proxy-name --organization=org-name
To describe an API proxy called ``proxy-name'' and include details on its
revisions, run:
$ {command} proxy-name --verbose
To describe an API proxy called ``proxy-name'' as a JSON object, run:
$ {command} proxy-name --format=json
"""
}
@classmethod
def Args(cls, parser):
parser.add_argument("--verbose", action="store_true",
help="Include proxy revision info in the description.")
if cls.ReleaseTrack() == base.ReleaseTrack.ALPHA:
parser.add_argument("--revision",
help="Include proxy revision info for a specific "
"revision ID in the description.")
resource_args.AddSingleResourceArgument(
parser,
"organization.api",
"API proxy to be described. To get a list of available API proxies, "
"run `{parent_command} list`.",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.api.Parse().AsDict()
result = apigee.APIsClient.Describe(identifiers)
# Must use vars(args) to check whether there's even a revision field in the
# parsed args namespace. It's only present for ALPHA track.
requested_revision = None
if "revision" in vars(args):
requested_revision = args.revision
# If the user didn't ask for revision data, the response from
# APIsClient.Describe() is good enough.
if requested_revision is None and not args.verbose:
return result
rev_nums = result["revision"]
if requested_revision is not None:
if requested_revision not in rev_nums:
message = "No revision %r among API %s's revisions: %s"%(
requested_revision, identifiers["apisId"], rev_nums)
raise exceptions.InvalidArgumentException("--revision", message)
# No need to check whether this revision exists within the original list;
# if there's no such revision, RevisionsClient will raise an appropriate
# error.
rev_nums = [requested_revision]
revisions = []
for revision in rev_nums:
identifiers["revisionsId"] = revision
revision_result = apigee.RevisionsClient.Describe(identifiers)
del revision_result["name"]
revisions.append(revision_result)
del result["revision"]
result["revisions"] = revisions
return result

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all Apigee API proxies in the relevant organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class List(base.ListCommand):
"""List Apigee API proxies."""
detailed_help = {
"EXAMPLES":
"""\
To list all API proxies for the active Cloud Platform project, run:
$ {command}
To list all API proxies in an organization called ``my-org'', run:
$ {command} --organization=my-org
To list all API proxies in an organization called ``my-org'', formatted as a
JSON array, run:
$ {command} --organization=my-org --format=json
"""}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization",
"Apigee organization whose API proxies should be listed. If "
"unspecified, the Cloud Platform project's associated organization "
"will be used.",
positional=False, required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
parser.display_info.AddFormat("list")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
result = apigee.APIsClient.List(identifiers)
if "proxies" not in result:
return []
return (item["name"] for item in result["proxies"])

View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to undeploy an Apigee API proxy from an environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Undeploy(base.SilentCommand):
"""Undeploy an Apigee API proxy from an environment."""
detailed_help = {
"EXAMPLES":
"""\
To undeploy an API proxy called ``my-api'' from the ``test'' environment of
the active Cloud Platform project, run:
$ {command} --environment=test --api=my-api
To undeploy revision 3 of an `my-api` from the `test` environment of the
organization named ``test-org'', run:
$ {command} 3 --organization=test-org --environment=test --api=my-api
"""}
@staticmethod
def Args(parser):
help_text = {
"api": "API proxy to be undeployed. Must currently be deployed. To "
"get a list of available deployed proxies, run "
"`{{grandparent_command}} deployments list --environment=ENV`.",
"environment": "Environment from which to undeploy the API proxy. To "
"get a list of available environments, run "
"`{{grandparent_command}} environments list`.",
"organization": "Apigee organization of the proxy and environment."
}
fallthroughs = [defaults.GCPProductOrganizationFallthrough(),
defaults.StaticFallthrough("revision", "auto")]
resource_args.AddSingleResourceArgument(
parser, "organization.environment.api.revision",
"API proxy revision to be undeployed and environment from which it "
"should be removed.\n\n"
"Revisions can either be a positive revision number, or the special "
"value ``auto'', which will undeploy whatever revision is currently "
"deployed. If revision is unspecified, the default is ``auto''.",
fallthroughs=fallthroughs, help_texts=help_text)
def Run(self, args):
"""Run the undeploy command."""
identifiers = args.CONCEPTS.revision.Parse().AsDict()
if identifiers["revisionsId"] == "auto":
del identifiers["revisionsId"]
defaults.FallBackToDeployedProxyRevision(identifiers)
return apigee.APIsClient.Undeploy(identifiers)

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 applications command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Applications(base.Group):
"""Manage third-party applications which call Apigee API proxies."""
detailed_help = {
"DESCRIPTION":
"""
{description}
`{command}` manages applications that want to use APIs exposed via
Apigee.
""",
"EXAMPLES":
"""
To get the names and UUIDs of all applications in the active Cloud
Platform project, run:
$ {command} list
To get a JSON representation of an application in the active Cloud
Platform project, including its API keys, run:
$ {command} describe APP_UUID --format=json
""",
}

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee application."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee application."""
detailed_help = {
"DESCRIPTION": """
{description}
`{command}` retrieves the application's details, including its
developer, credentials, API products, and other information.
""",
"EXAMPLES": """
To describe an application for the active Cloud Platform project whose
UUID is ``46d6151e-0000-4dfa-b9c7-c03b8b58bb2f'', run:
$ {command} 46d6151e-0000-4dfa-b9c7-c03b8b58bb2f
To describe that application in the Apigee organization ``my-org'',
formatted as a JSON object, run:
$ {command} 46d6151e-0000-4dfa-b9c7-c03b8b58bb2f --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization.app",
"Application to be described. To get a list of available applications, "
"run `{parent_command} list`.",
argument_name="APPLICATION",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.application.Parse().AsDict()
return apigee.ApplicationsClient.Describe(identifiers)

View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list Apigee applications."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
# Placeholder developer identifier that should include all developers. No real
# developer ID should have / in it, as they're email addresses.
ANY_DEVELOPER = "/ANY/"
class List(base.ListCommand):
"""List Apigee applications."""
detailed_help = {
"EXAMPLES":
"""
To list all Apigee applications in the active Cloud Platform project,
run:
$ {command}
To list all Apigee applications belonging to the developer
``horse@example.com'' in an Apigee organization called ``my-org'',
formatted as JSON, run:
$ {command} --developer=horse@example.com --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
fallthroughs = [
defaults.GCPProductOrganizationFallthrough(),
defaults.StaticFallthrough("developer", ANY_DEVELOPER)
]
resource_args.AddSingleResourceArgument(
parser,
"organization.developer",
"Apigee organization, and optionally developer, whose applications "
"should be listed. If developer is not specified, all developers will "
"be listed.\n\n"
"To get a list of valid developers, run:\n\n"
" $ {grandparent_command} developers list\n\n",
positional=False,
fallthroughs=fallthroughs)
parser.display_info.AddFormat("table(appId, name)")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.developer.Parse().AsDict()
if identifiers["developersId"] == ANY_DEVELOPER:
del identifiers["developersId"]
return apigee.ApplicationsClient.List(identifiers)

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 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 archives command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.projects import util
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Archives(base.Group):
"""Manage Apigee archive deployments."""
detailed_help = {
"EXAMPLES": """
To deploy a local archive deployment remotely to the management plane
in the ``test'' environment, run:
$ {command} deploy --environment=test
To list all archive deployments in the ``dev'' environment, run:
$ {command} list --environment=dev
To describe the archive deployment with id ``abcdef01234'' in the
``demo'' environment of the ``my-org'' Apigee organization, run:
$ {command} describe abcdef01234 --environment=demo --organization=my-org
To update the labels of the archive deployment with id
``uvxwzy56789'' in the ``test'' environment, run:
$ {command} update uvxwzy56789 --environment=demo --update-labels=foo=1,bar=2
""",
}

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google Inc. 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.
"""Command to delete an archive deployment in an Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Delete(base.DeleteCommand):
"""Delete an Apigee archive deployment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` deletes an Apigee archive deployment.""",
"EXAMPLES":
"""\
To delete an archive deployment with the ID ``abcdefghijkl123456'' in the
environment called ``my-env'' using the active Cloud Platform project, run:
$ {command} abcdefghijkl123456 --environment=my-env
To delete an archive deployment with the ID ``mnopqurstuvw654321'', in an
environment called ``my-env'', in an organization called ``my-org'', run:
$ {command} mnopqurstuvw654321 --environment=my-env --organization=my-org
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.archive_deployment",
"Apigee archive deployment to delete.",
argument_name="archive_deployment",
positional=True,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.archive_deployment.Parse().AsDict()
archive_id = identifiers["archiveDeploymentsId"]
msg = "Archive deployment [{}] will be deleted.".format(archive_id)
if console_io.PromptContinue(message=msg):
apigee.ArchivesClient.Delete(identifiers)
log.status.Print("Archive deployment [{}] deleted.".format(archive_id))

View File

@@ -0,0 +1,204 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google Inc. 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.
"""Command to deploy an Apigee archive deployment to an environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import archives as cmd_lib
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import errors
from googlecloudsdk.command_lib.apigee import resource_args
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core.util import files
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Deploy(base.DescribeCommand):
"""Deploy an Apigee archive deployment to an environment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` installs an archive deployment in an Apigee environment.
By default, the archive deployment will be deployed on the remote management
plane for the specified Apigee organization. To deploy on a locally running
Apigee emulator, use the `--local` flag.
""",
"EXAMPLES":
"""\
To deploy the contents of the current working directory as an archive
deployment to an environment named ``my-test'', given that the Cloud Platform
project has been set in gcloud settings, run:
$ {command} --environment=my-test
To deploy an archive deployment from a local directory other than the current
working directory, to an environment named ``my-demo'' in an organization
belonging to a Cloud Platform project other than the one set in gcloud
settings, named ``my-org'', run:
$ {command} --organization=my-org --environment=my-demo --source=/apigee/dev
To deploy the contents of the current working directory as an archive
deployment, with the user-defined labels ``my-label1=foo'' and
``my-label2=bar'', to an environment named ``my-test'', given that the Cloud
Platform project has been set in gcloud settings, run:
$ {command} --environment=my-test --labels=my-label1=foo,my-label2=bar
"""
}
@staticmethod
def Args(parser):
fallthroughs = [defaults.GCPProductOrganizationFallthrough()]
resource_args.AddSingleResourceArgument(
parser,
resource_path="organization.environment",
help_text=("Apigee environment in which to deploy the archive "
"deployment."),
fallthroughs=fallthroughs,
positional=False,
required=True)
# Create a argument group to manage that only one of either --source or
# --bundle-file flags are provided on the command line.
source_input_group = parser.add_group(mutex=True, help="Source input.")
source_input_group.add_argument(
"--source",
required=False,
type=files.ExpandHomeDir,
help="The source directory of the archive to upload.")
source_input_group.add_argument(
"--bundle-file",
required=False,
type=files.ExpandHomeDir,
help="The zip file containing an archive to upload.")
parser.add_argument(
"--async",
action="store_true",
dest="async_",
help=("If set, returns immediately and outputs a description of the "
"long running operation that was launched. Else, `{command}` "
"will block until the archive deployment has been successfully "
"deployed to the specified environment.\n\n"
"To monitor the operation once it's been launched, run "
"`{grandparent_command} operations describe OPERATION_NAME`."))
# This adds the --labels flag.
labels_util.AddCreateLabelsFlags(parser)
def _GetUploadUrl(self, identifiers):
"""Gets the signed URL for uploading the archive deployment.
Args:
identifiers: A dict of resource identifers. Must contain "organizationsId"
and "environmentsId"
Returns:
A str of the upload URL.
Raises:
googlecloudsdk.command_lib.apigee.errors.RequestError if the "uploadUri"
field is not included in the GetUploadUrl response.
"""
get_upload_url_resp = apigee.ArchivesClient.GetUploadUrl(identifiers)
if "uploadUri" not in get_upload_url_resp:
raise errors.RequestError(
resource_type="getUploadUrl",
resource_identifier=identifiers,
body=get_upload_url_resp,
user_help="Please try again.")
return get_upload_url_resp["uploadUri"]
def _UploadArchive(self, upload_url, zip_file_path):
"""Issues an HTTP PUT call to the upload URL with the zip file payload.
Args:
upload_url: A str containing the full upload URL.
zip_file_path: A str of the local path to the zip file.
Raises:
googlecloudsdk.command_lib.apigee.errors.HttpRequestError if the response
status of the HTTP PUT call is not 200 (OK).
"""
upload_archive_resp = cmd_lib.UploadArchive(upload_url, zip_file_path)
if not upload_archive_resp.ok:
raise errors.HttpRequestError(upload_archive_resp.status_code,
upload_archive_resp.reason,
upload_archive_resp.content)
def _DeployArchive(self, identifiers, upload_url, labels):
"""Creates the archive deployment.
Args:
identifiers: A dict of resource identifers. Must contain "organizationsId"
and "environmentsId"
upload_url: A str containing the full upload URL.
labels: A dict of the key/value pairs to add as labels.
Returns:
A dict containing the operation metadata.
"""
post_data = {}
post_data["gcs_uri"] = upload_url
if labels:
post_data["labels"] = {}
for k, v in labels.items():
post_data["labels"][k] = v
api_response = apigee.ArchivesClient.CreateArchiveDeployment(
identifiers, post_data)
operation = apigee.OperationsClient.SplitName(api_response)
return operation
def Run(self, args):
"""Run the deploy command."""
identifiers = args.CONCEPTS.environment.Parse().AsDict()
labels_arg = labels_util.GetUpdateLabelsDictFromArgs(args)
local_dir_archive = None
try:
local_dir_archive = cmd_lib.LocalDirectoryArchive(args.source)
if args.bundle_file:
local_dir_archive.ValidateZipFilePath(args.bundle_file)
zip_file_path = args.bundle_file
else:
zip_file_path = local_dir_archive.Zip()
upload_url = self._GetUploadUrl(identifiers)
self._UploadArchive(upload_url, zip_file_path)
operation = self._DeployArchive(identifiers, upload_url, labels_arg)
if "organization" not in operation or "uuid" not in operation:
raise waiter.OperationError(
"Unknown operation response: {}".format(operation))
if "warnings" in operation["metadata"]:
for warning in operation["metadata"]["warnings"]:
log.warning(warning)
log.info("Started archives deploy operation %s", operation["name"])
if args.async_:
return operation
waiter.WaitFor(
apigee.LROPoller(operation["organization"]),
operation["uuid"],
message="Waiting for operation [{}] to complete".format(
operation["uuid"]),
wait_ceiling_ms=5000,
)
finally:
if local_dir_archive and hasattr(local_dir_archive, "Close"):
local_dir_archive.Close()

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google Inc. 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.
"""Command to describe an archive deployment in an Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import archives as archive_helper
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Describe(base.DescribeCommand):
"""Describe an Apigee archive deployment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` shows metadata about an Apigee archive deployment.""",
"EXAMPLES":
"""\
To describe an archive deployment with the id ``abcdef1234'' in the Apigee
environment called ``my-env'' using the active Cloud Platform project, run:
$ {command} abcdef1234 --environment=my-env
To describe an archive deployment with the id ``1234abcdef'', in the Apigee
environment called ``my-env'', in an organization called ``my-org'', as a JSON
object, run:
$ {command} 1234abcdef --environment=my-env --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.archive_deployment",
help_text="Archive deployment to be described. To get a list of "
"available archive deployments, run `{parent_command} list`.",
argument_name="archive_deployment",
positional=True,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.archive_deployment.Parse().AsDict()
org = identifiers["organizationsId"]
archive_name = (
"organizations/{}/environments/{}/archiveDeployments/{}".format(
org, identifiers["environmentsId"],
identifiers["archiveDeploymentsId"]))
archive_list_response = apigee.ArchivesClient.List(identifiers)
if not archive_list_response:
return apigee.ArchivesClient.Describe(identifiers)
extended_archives = archive_helper.ListArchives(org).ExtendedArchives(
archive_list_response)
for a in extended_archives:
if a["name"] == archive_name:
return a
return apigee.ArchivesClient.Describe(identifiers)

View File

@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google Inc. 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.
"""Command to list all Apigee archive deployments in an environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import archives as archive_helper
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class List(base.ListCommand):
"""List Apigee archive deployments."""
detailed_help = {
"EXAMPLES":
"""\
To list all archive deployments, in an environment called ``my-env'', for the
active Cloud Platform project, run:
$ {command} --environment=my-env
To list all archive deployments, for an environment named ``my-env'', in an
organization called ``my-org'', run:
$ {command} --environment=my-env --organization=my-org
To list all archive deployments formatted as a JSON array, run:
$ {command} --environment=my-env --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.environment",
"Apigee environment whose archive deployments should be listed.",
positional=False,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
# The response is a JSON array of archive deployment descriptors, so the
# array needs to be flattened to display as a table.
parser.display_info.AddFlatten(["archiveDeployments[]"])
# Cloud SDK projections can be used to format each column of the display
# table:
# https://cloud.google.com/sdk/gcloud/reference/topic/projections
# The "ARCHIVE ID" column scopes the resource path in the "name" field of
# the API response to only show the id of the archive deployment.
archive_id_col = ("archiveDeployments.name.scope(archiveDeployments)"
":label='ARCHIVE ID'")
# The "ENVIORNMENT" column scopes the resource path in the "name" field of
# the API response to only show the Apigee environment id.
env_id_col = ("archiveDeployments.name.scope(environments).segment(0)"
":label=ENVIRONMENT")
# The "CREATED AT" column formats the posix epoch in the "createdAt" field
# of the API response into a human-readable date format.
created_col = ("archiveDeployments.createdAt.date("
"format='%Y-%m-%d %H:%M:%S %Z', unit=1000000, tz=LOCAL)"
":label='DEPLOYED AT'")
# The labels field is a list of key/value pairs so it is flattened to
# display in the table.
labels_col = "archiveDeployments.labels.flatten()"
# The status column uses operation metadata and timestamps to determine
# the current status of the archive deployment.
status_col = ("archiveDeployments.operationStatus:label='OPERATION STATUS'")
cols = ", ".join(
[archive_id_col, env_id_col, created_col, labels_col, status_col])
# Format the column definitions into a table.
table_fmt = "table({})".format(cols)
parser.display_info.AddFormat(table_fmt)
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.environment.Parse().AsDict()
org = identifiers["organizationsId"]
archive_response = apigee.ArchivesClient.List(identifiers)
extended_archives = archive_helper.ListArchives(org).ExtendedArchives(
archive_response)
return {"archiveDeployments": extended_archives}

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2021 Google Inc. 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.
"""Command to update an archive deployment in an Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
from googlecloudsdk.command_lib.util.args import labels_util
# DEVELOPER NOTE: This command inherits from the base.DescribeCommand (as
# opposed to the base.UpdateCommand) to get the print functionality of the
# return value (the base.UpdateCommand is silent) in order to print the updated
# archive deployment after the command is run.
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Update(base.DescribeCommand):
"""Update an existing Apigee archive deployment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` updates an Apigee archive deployment.""",
"EXAMPLES":
"""\
To update the ``tag'' and ``rev'' labels of an archive deployment with the id
``abcdef01234'' in the Apigee environment called ``my-env'' using the active
Cloud Platform project, run:
$ {command} abcdef01234 --environment=my-env --update-labels=tag=my-tag,rev=1234
To remove the ``dev'' label on an archive deployment with the id
``uvwxyz56789'', in the Apigee environment called ``my-env'', in an
organization called ``my-org'', run:
$ {command} uvwxyz56789 --environment=my-env --organization=my-org --remove-labels=dev
To clear all labels on an archive deployment with the id ``mnop4321'', in
the Apigee environment called ``my-env'', in an organization called
``my-org'', and return the updated archive deployment as a JSON object, run:
$ {command} mnop4321 --environment=my-env --organization=my-org --clear-labels --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.archive_deployment",
help_text="Archive deployment to update. To get a list of "
"existing archive deployments, run `{parent_command} list`.",
argument_name="archive_deployment",
positional=True,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
# This adds the --update-labels, --remove-labels and --clear-labels flags.
labels_util.AddUpdateLabelsFlags(parser)
def Run(self, args):
"""Run the update command."""
labels_util.GetAndValidateOpsFromArgs(args)
identifiers = args.CONCEPTS.archive_deployment.Parse().AsDict()
# First get the existing lables by calling describe on the current archive.
existing_archive = apigee.ArchivesClient.Describe(identifiers)
# Modify the label set based on provided flag values.
if "labels" in existing_archive and not args.clear_labels:
new_labels = existing_archive["labels"]
else:
new_labels = {}
if args.update_labels:
new_labels.update(args.update_labels)
if args.remove_labels:
for label in args.remove_labels:
if label in new_labels:
del new_labels[label]
labels_proto = {"labels": new_labels}
return apigee.ArchivesClient.Update(identifiers, labels_proto)

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 deployments command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Deployments(base.Group):
"""Manage deployments of Apigee API proxies in runtime environments."""
detailed_help = {
"DESCRIPTION": """
{description}
`{command}` contains commands for enumerating and checking the status
of deployments of proxies to runtime environments.
""",
"EXAMPLES": """
To list all deployments for the active Cloud Platform project, run:
$ {command} list
To list all deployments in a particular environment of a particular
Apigee organization, run:
$ {command} list --environment=ENVIRONMENT --organization=ORG_NAME
To get the status of a specific deployment as a JSON object, run:
$ {command} describe --api=API_NAME --environment=ENVIRONMENT --format=json
""",
}

View File

@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee API deployment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import errors
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee API proxy deployment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` retrieves the status of a specific API proxy's deployment to a
specific environment.
""",
"EXAMPLES":
"""\
To get the status of a deployment in the active Cloud Platform project, which
deploys ``my-proxy'' into the ``prod'' environment, run:
$ {command} --api=my-proxy --environment=prod
To get the status of that deployment as a JSON object, run:
$ {command} --api=my-proxy --environment=prod --format=json
To get the status of a deployment in an organization called ``my-org'', which
deploys version 3 of the proxy ``my-proxy'' into the ``test'' environment,
run:
$ {command} 3 --organization=my-org --environment=test --api=my-proxy
"""
}
@staticmethod
def Args(parser):
help_text = {
"api": "Deployed API proxy.",
"environment": "Environment in which the proxy was deployed.",
"organization": ("Apigee Organization of the proxy and environment. If "
"unspecified, the Cloud Platform project's associated "
"organization will be used."),
}
fallthroughs = [
defaults.GCPProductOrganizationFallthrough(),
defaults.StaticFallthrough("revision", "auto")
]
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.api.revision",
"""\
API proxy revision and environment of the deployment to be described.
To get a list of deployed proxies and their environments, run:
$ {parent_command} list
REVISION can either be a positive revision number, or the special value
``auto'', which will choose whichever revision of API is currently deployed in
ENVIRONMENT, or return an error if more than one revision is deployed.
If REVISION is unspecified, the default is ``auto''.
""",
fallthroughs=fallthroughs,
help_texts=help_text)
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.revision.Parse().AsDict()
if identifiers["revisionsId"] == "auto":
del identifiers["revisionsId"]
defaults.FallBackToDeployedProxyRevision(identifiers)
# Deployments have no identifier of their own, so the API to get deployment
# details looks like a List call with all possible parents specified.
deployments = apigee.DeploymentsClient.List(identifiers)
if not deployments:
raise errors.EntityNotFoundError("deployment", identifiers, "GET")
return deployments[0]

View File

@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list Apigee API proxy deployments."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class List(base.ListCommand):
"""List Apigee API proxy deployments."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` lists deployments of API proxies, optionally filtered by
environment, proxy name, proxy revision, or a combination of these.
""",
"EXAMPLES":
"""
To list all deployments for the active Cloud Platform project, run:
$ {command}
To list all deployments in an Apigee organization called ``my-org'',
run:
$ {command} --organization=my-org
To list all deployments of an API proxy called ``my-proxy'' in the
active Cloud Platform project, run:
$ {command} --api=my-proxy
To list all deployments to the ``test'' environment of the active
Cloud Platform project, formatted as a JSON array, run:
$ {command} --environment=test --format=json
To list all deployments of ``my-proxy'' to the ``test'' environment in
an Apigee organization called ``my-org'', run:
$ {command} --organization=my-org --api=my-proxy --environment=test
"""
}
@staticmethod
def Args(parser):
help_text = {
"api": ("The API proxy whose deployments should be listed. If not "
"provided, all proxies will be listed. To get a list of "
"existing API proxies, run "
"`{{grandparent_command}} apis list`."),
"environment": ("The environment whose deployments should be listed. "
"If not provided, all environments will be listed. "
"To get a list of available environments, run "
"`{{grandparent_command}} environments list`."),
"organization": ("The organization whose deployments should be listed."
"If unspecified, the Cloud Platform project's "
"associated organization will be used."),
}
# When Calliope parses a resource's resource arguments, it interprets them
# as an all-or-nothing whole; if any of them are not provided the whole
# resource defaults to None.
#
# Thus, to make individual parts of the search path optional for the user
# while accepting others, fallthrough logic for those arguments must be
# present to return a placeholder value that can be recognized after parsing
# and interpreted as "no value provided".
#
# Unfortunately, `None` won't work as the placeholder, because a fallthrough
# class returning "None" means it was unable to provide a value at all.
# Thus, some other value must be chosen.
fallthroughs = [
defaults.GCPProductOrganizationFallthrough(),
# For `revision`, the placeholder MUST be a string, because gcloud
# concepts will try to parse it as a URL or fully qualified path, and
# will choke on non-string values. This should be safe as all legitimate
# revisions are numeric.
defaults.StaticFallthrough("revision", "all"),
# For other arguments, the placeholder must not be a string; any string
# provided here might collide with a real environment or API name. Use
# the Python builtin function all() as a convenient, idiomatic
# alternative.
defaults.StaticFallthrough("environment", all),
defaults.StaticFallthrough("api", all),
]
resource_args.AddSingleResourceArgument(
parser,
"organization.environment.api.revision",
"API proxy revision and environment whose deployments should be "
"listed. Providing a REVISION is only valid if API is also specified. "
"If no REVISION is provided, all deployed revisions that match the "
"other arguments will be included.",
positional=False,
required=True,
fallthroughs=fallthroughs,
help_texts=help_text)
parser.display_info.AddFormat("table(environment,apiProxy,revision)")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.revision.Parse().AsDict()
if identifiers["apisId"] is all:
if identifiers["revisionsId"] != "all":
raise exceptions.RequiredArgumentException(
"--api", "Filtering by revision requires specifying its API.")
del identifiers["apisId"]
if identifiers["environmentsId"] is all:
del identifiers["environmentsId"]
if identifiers["revisionsId"] == "all":
del identifiers["revisionsId"]
return apigee.DeploymentsClient.List(identifiers)

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 developers command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Developers(base.Group):
"""Manage Apigee developers."""
detailed_help = {
"DESCRIPTION": """
{description}
`{command}` manages developers that want to use APIs exposed via
Apigee in their applications.
""",
"EXAMPLES": """
To list the email addresses of all the developers in the active Cloud
Platform project, run:
$ {command} list
To get that list as a JSON array and only include developers with
``example.com'' addresses, run:
$ {command} list --format=json --filter="email:(@example.com)"
To get details about a specific developer in the active Cloud Platform
project, run:
$ {command} describe DEVELOPER_EMAIL
""",
}

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee developer."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee developer."""
detailed_help = {
"DESCRIPTION": """
{description}
`{command}` retrieves the developer's details, including the
developer's name, email address, apps, and other information.
""",
"EXAMPLES": """
To describe a developer for the active Cloud Platform project whose
email address is ``larry@example.com'', run:
$ {command} larry@example.com
To describe that developer in the Apigee organization ``my-org'',
formatted as a JSON object, run:
$ {command} larry@example.com --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization.developer",
"Email address of the developer to be described. To get a list of "
"available developers, run `{parent_command} list`.",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.developer.Parse().AsDict()
return apigee.DevelopersClient.Describe(identifiers)

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all developers in the relevant Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class List(base.ListCommand):
"""List Apigee developers by email address."""
detailed_help = {
"DESCRIPTION":
"{description}",
"EXAMPLES":
"""
To list all developers for the active Cloud Platform project, run:
$ {command}
To list all developers in an Apigee organization called ``my-org'',
formatted as JSON objects, run:
$ {command} --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization",
"Apigee organization whose developers should be listed. If "
"unspecified, the Cloud Platform project's associated organization "
"will be used.",
positional=False,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
parser.display_info.AddFormat("list(email)")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
return apigee.DevelopersClient.List(identifiers)

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 environments command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Environments(base.Group):
"""Manage Apigee environments."""
detailed_help = {
"EXAMPLES":
"""\
To list all environments for the active Cloud Platform project, run:
$ {command} list
To get a JSON array of all environments in an Apigee organization called
``my-org'', run:
$ {command} list --organization=my-org --format=json
"""}

View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an environment in the relevant Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Describe(base.DescribeCommand):
"""Describe an Apigee deployment environment."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` shows metadata about an Apigee environment.""",
"EXAMPLES":
"""\
To describe an environment called ``my-env'' for the active Cloud Platform
project, run:
$ {command} my-env
To describe an environment called ``my-env'', in an organization called
``my-org'', as a JSON object, run:
$ {command} my-env --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.environment",
"Apigee environment to be described. To get a list of available "
"environments, run `{parent_command} list`.",
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.environment.Parse().AsDict()
return apigee.EnvironmentsClient.Describe(identifiers)

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all environments in the relevant Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class List(base.ListCommand):
"""List Apigee deployment environments."""
detailed_help = {
"EXAMPLES":
"""\
To list all environments for the active Cloud Platform project, run:
$ {command}
To get a JSON array of all environments in an organization called ``my-org'',
run:
$ {command} --organization=my-org --format=json
"""}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization",
"Apigee organization whose environments should be listed. If "
"unspecified, the Cloud Platform project's associated organization "
"will be used.",
positional=False, required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
parser.display_info.AddFormat("list")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
return apigee.EnvironmentsClient.List(identifiers)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 operations command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Operations(base.Group):
"""Manage Apigee long running operations."""

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee long running operation."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee long running operation."""
detailed_help = {
"EXAMPLES":
"""\
To describe an operation with UUID ``e267d2c8-04f4-0000-b890-a241de823b0e''
given that its matching Cloud Platform project has been set in gcloud
settings, run:
$ {command} e267d2c8-04f4-0000-b890-a241de823b0e
To describe an operation with UUID ``e267d2c8-04f4-0000-b890-a241de823b0e''
within an organization named ``my-org'', formatted as JSON, run:
$ {command} e267d2c8-04f4-0000-b890-a241de823b0e --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization.operation",
"Operation to be described. To get a list of available operations, run "
"`{{parent_command}} list`.",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.operation.Parse().AsDict()
return apigee.OperationsClient.Describe(identifiers)

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all long running operations in the relevant organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
RESPONSE_CONTENT_FIELD = "operations"
class List(base.ListCommand):
"""List Apigee long running operations."""
detailed_help = {
"EXAMPLES":
"""\
To list all operations for the active Cloud Platform project, run:
$ {command}
To list all in-progress operations in an Apigee organization called
``my-org'', formatted as a JSON array, run:
$ {command} --organization=my-org --filter="metadata.state=IN_PROGRESS" --format=json
"""}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization",
"Organization whose operations should be listed. If unspecified, the "
"Cloud Platform project's associated organization will be used.",
positional=False, required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
parser.display_info.AddFormat("table(uuid, organization, metadata.state)")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
return apigee.OperationsClient.List(identifiers)

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 organizations command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Organizations(base.Group):
"""Manage Apigee organizations."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` contains commands for managing Apigee organizations, the
highest-level grouping of Apigee objects. All API proxies, environments, and
so forth each belong to one organization.
Apigee organizations are distinct from Cloud Platform organizations, being
more similar to Cloud Platform projects in scope and purpose.
""",
"EXAMPLES":
"""\
To list all accessible organizations and their associated Cloud Platform projects, run:
$ {command} list
To get a JSON array of all organizations whose Cloud Platform project names
contain the word ``sandbox'', run:
$ {command} list --format=json --filter="project:(sandbox)"
"""}

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to delete an Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import resource_args
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Delete(base.DescribeCommand):
"""Delete an Apigee organization."""
detailed_help = {
"DESCRIPTION":
"""
{description}
`{command}` deletes an organization and all resources inside it. This
is currently only supported for trial organizations.
This is a long running operation. Once organization provisioning has
begun, `{command}` will exit, returning the operation's ID and initial
status. To continue monitoring the operation, run
`{grandparent_command} operations describe OPERATION_NAME`.
""",
"EXAMPLES":
"""
To delete an organization called ``my-org'', run:
$ {command} my-org
To delete an organization called ``my-org'', and print only the name
of the launched operation, run:
$ {command} my-org --format="value(name)"
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization", "The trial organization to be deleted.")
def Run(self, args):
"""Run the delete command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
return apigee.OrganizationsClient.Delete(identifiers)

View File

@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all Apigee organizations to which the user has access."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
@base.UniverseCompatible
class List(base.ListCommand):
"""List Apigee organizations and their paired Cloud Platform projects."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` lists all organizations to which the user's `gcloud auth`
credentials have access, even if they don't match the active Cloud Platform
project.
Apigee organizations are distinct from Cloud Platform organizations, and
usually have a one-to-one relationship with Cloud Platform projects.
""",
"EXAMPLES":
"""\
To list all accessible organizations and their associated Cloud Platform projects, run:
$ {command}
To get a JSON array of all organizations whose Cloud Platform project names
contain the word ``sandbox'', run:
$ {command} --format=json --filter="project:(sandbox)"
"""}
@staticmethod
def Args(parser):
parser.display_info.AddFormat("table(name, project.flatten())")
def Run(self, args):
"""Run the list command."""
if properties.VALUES.api_endpoint_overrides.apigee.Get():
result = apigee.OrganizationsClient.List(vars(args))
else:
result = apigee.OrganizationsClient.ListOrganizationsGlobal()
if "organizations" in result:
for organization in result["organizations"]:
yield {
"name": organization["organization"],
"project": organization["projectIds"]
}

View File

@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to provision an Apigee SaaS organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Provision(base.DescribeCommand):
"""Provision an Apigee SaaS organization."""
detailed_help = {
"DESCRIPTION":
"""\
{description}
`{command}` creates an Apigee organization and populates it with the
necessary child resources to be immediately useable.
This is a long running operation and could take anywhere from 10 minutes to 1
hour to complete.
At the moment, only trial organizations are supported.
""",
"EXAMPLES":
"""
To provision an organization for the active Cloud Platform project,
attached to a network named ``default'' run:
$ {command} --authorized-network=default
To provision an organization asynchronously and print only the name of
the launched operation, run:
$ {command} --authorized-network=default --async --format="value(name)"
To provision an organization for the project named ``my-proj'', with
analytics and runtimes located in ``us-central1'', run:
$ {command} --authorized-network=default --project=my-proj --analytics-region=us-central1 --runtime-location=us-central1-a
"""
}
@staticmethod
def Args(parser):
parser.add_argument(
"--authorized-network",
required=True,
help="""\
Name of the network to which the provisioned organization should be attached.
This must be a VPC network peered through Service Networking. To get a list
of existing networks, run:
$ gcloud compute networks list
To check whether a network is peered through Service Networking, run:
$ gcloud services vpc-peerings list --network=NETWORK_NAME --service=servicenetworking.googleapis.com
To create a new network suitable for Apigee provisioning, choose a name for
the network and address range, and run:
$ gcloud compute networks create NETWORK_NAME --bgp-routing-mode=global --description='network for an Apigee trial'
$ gcloud compute addresses create ADDRESS_RANGE_NAME --global --prefix-length=16 --description="peering range for an Apigee trial" --network=NETWORK_NAME --purpose=vpc_peering
$ gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --network=NETWORK_NAME --ranges=ADDRESS_RANGE_NAME"""
)
parser.add_argument(
"--analytics-region",
help=("Primary Cloud Platform region for analytics data storage. For "
"valid values, see: "
"https://docs.apigee.com/hybrid/latest/precog-provision.\n\n"
"If unspecified, the default is ``us-west1''"))
parser.add_argument(
"--runtime-location",
help=("Cloud Platform location for the runtime instance. For trial "
"organizations, this is a compute zone. To get a list of valid "
"zones, run `gcloud compute zones list`. If unspecified, the "
"default is ``us-west1-a''."))
parser.add_argument(
"--async",
action="store_true",
dest="async_",
help=("If set, returns immediately and outputs a description of the "
"long running operation that was launched. Else, `{command}` "
"will block until the organization has been provisioned.\n\n"
"To monitor the operation once it's been launched, run "
"`{grandparent_command} operations describe OPERATION_NAME`."))
def Run(self, args):
"""Run the provision command."""
org_info = {"authorizedNetwork": args.authorized_network}
if args.analytics_region:
org_info["analyticsRegion"] = args.analytics_region
if args.runtime_location:
org_info["runtimeLocation"] = args.runtime_location
project = properties.VALUES.core.project.Get()
if project is None:
exceptions.RequiredArgumentException(
"--project",
"Must provide a GCP project in which to provision the organization.",
)
location = "global"
if properties.VALUES.api_endpoint_overrides.apigee.Get():
location = None
operation = apigee.ProjectsClient.ProvisionOrganization(
project, org_info, location
)
apigee.OperationsClient.SplitName(operation)
if args.async_:
return operation
log.info("Started provisioning operation %s", operation["name"])
return waiter.WaitFor(
apigee.LROPoller(operation["organization"]),
operation["uuid"],
"Provisioning organization",
max_wait_ms=60 * 60 * 1000)

View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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 products command group for the Apigee CLI."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Products(base.Group):
"""Manage Apigee API products."""
detailed_help = {
"DESCRIPTION": """
{description}
`{command}` manipulates API products. These are collections of
deployed API resources, combined with quota settings and metadata,
used to deliver customized and productized API bundles to the
developer community.
""",
"EXAMPLES": """
To list all API products in the active Cloud Platform project, run:
$ {command} list
To create an API product named ``my-apis'' by answering interactive
prompts about its included proxies and access policies, run:
$ {command} create my-apis
To create an API product named ``prod-apis'' that makes every API
proxy deployed to the ``prod'' environment publicly available, run:
$ {command} create prod-apis --environments=prod --all-proxies --public-access
To get a JSON object describing an existing API product, run:
$ {command} describe PRODUCT_NAME --organization=ORG_NAME --format=json
To add another API proxy to an existing API product, run:
$ {command} update PRODUCT_NAME --add-api=API_NAME
To edit the publicly visible name and description of an API product,
run:
$ {command} update PRODUCT_NAME --display-name="New Name" --description="A new description of this product."
To make an existing product publicly visible and automatically allow
developers access to it, run:
$ {command} update PRODUCT_NAME --public-access --automatic-approval
To delete an existing API product, run:
$ {command} delete PRODUCT_NAME
""",
}

View File

@@ -0,0 +1,445 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to create an Apigee API product."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.apigee import argument_groups
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import prompts
from googlecloudsdk.command_lib.apigee import resource_args
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
class Deploy(base.DescribeCommand):
"""Create an Apigee API product."""
detailed_help = {
"DESCRIPTION":
"""
Create an Apigee API product.
`{command}` publishes a collection of API proxy resources as an API
product.
API products combine their underlying API proxies with quota settings
and metadata, to deliver customized and productized API bundles to
the developer community.
API products enable the repackaging of APIs on-the-fly, without having
to do any additional coding or configuration. Apigee recommends
starting with a simple API product including only required elements,
and then provisioning credentials to apps to enable them to start
testing those APIs.
At minimum, a new API product requires an internal name, access
policy, and declaration of what environments and API proxies to
include in the product. If these aren't provided, interactive calls
will prompt for the missing values, and non-interactive calls will
fail.
""",
"EXAMPLES":
"""
To create a basic API product in the active Cloud Platform project by
answering interactive prompts, run:
$ {command}
To create an API product that publicly exposes all API proxies
deployed to the ``prod'' environment, run:
$ {command} kitchen-sink --environments=prod --all-proxies --public-access
To require manual approval of developers before they can access the
new API product, run:
$ {command} kitchen-sink --environments=prod --all-proxies --public-access --manual-approval
To hide the new API product while still making it accessible to
developers, run:
$ {command} kitchen-sink --environments=prod --all-proxies --private-access
To restrict the new API product to internal users only, run:
$ {command} kitchen-sink --environments=prod --all-proxies --internal-access
To expose all API proxies that are deployed to a URI fragment
beginning with ``/v1'' or ``/v0'', run:
$ {command} legacy --all-environments --resources="/v0/**#/v1/**" --public-access
To expose a few specific API proxies on all URI paths where they're
deployed, run:
$ {command} consumer --environments=prod --apis=menu,cart,delivery-tracker --public-access
To expose only those API calls that match both a set of API proxies
and a set of API resources, run:
$ {command} legacy-consumer --environments=prod --apis=menu,cart,delivery-tracker --resources="/v0/**#/v1/**" --public-access
To impose a quota of 50 calls per half-hour on a new all-inclusive API
product, and output the new API product as a JSON object, run:
$ {command} kitchen-sink --environments=prod --all-proxies --public-access --quota=50 --quota-interval=30 --quota-unit=minute --format=json
To specify a human-friendly display name and description for the
product, run:
$ {command} consumer --environments=prod --apis=menu,cart,delivery-tracker --public-access --display-name="Consumer APIs" --description="APIs for the consumer side of the delivery network: ordering food and tracking deliveries."
"""
}
@staticmethod
def Args(parser):
# Can't use fallthroughs for optional resource arguments, as they won't run
# unless at least one argument from the set is provided.
resource_args.AddSingleResourceArgument(
parser,
"organization.product",
"API product to be created. Characters in a product's internal name "
"are restricted to: ```A-Za-z0-9._-$ %```.",
validate=True,
argument_name="INTERNAL_NAME",
required=False)
environment_group = parser.add_mutually_exclusive_group()
environment_group.add_argument(
"--environments",
metavar="ENVIRONMENT",
type=arg_parsers.ArgList(min_length=1),
help=("Environments to which the API product is bound. Requests to "
"environments that are not listed are rejected, preventing "
"developers from accessing those resources through API Proxies "
"deployed in another environment.\n\n"
"For example, this can prevent resources associated with API "
"proxies in a ``prod'' environment from also granting access to "
"matching API proxies deployed in a ``test'' environment.\n\n"
"To get a list of available environments, run:\n\n"
" $ {grandparent_command} environments list"))
environment_group.add_argument(
"--all-environments",
dest="environments",
action="store_const",
const=[],
help="Make all environments accessible through this API product.")
parser.add_argument(
"--display-name",
help=("Name to be displayed in the UI or developer portal to "
"developers registering for API access."))
access_group = parser.add_mutually_exclusive_group()
access_group.add_argument(
"--public-access",
dest="access",
action="store_const",
const="public",
help=("Make this API product visible to developers in the Apigee "
"developer portal."))
access_group.add_argument(
"--private-access",
dest="access",
action="store_const",
const="private",
help=("Hide this API product in the developer portal but make it "
"accessible by external developers."))
access_group.add_argument(
"--internal-access",
dest="access",
action="store_const",
const="internal",
help="Prevent external access to this API product.")
proxies_mutex_group = parser.add_mutually_exclusive_group(
"Arguments specifying which API proxies and resources to expose.")
proxies_mutex_group.add_argument(
"--all-proxies",
action="store_true",
help=("Expose all available API proxies and their resources. Must be "
"explicitly specified if neither `--apis` nor `--resources` is "
"provided."))
proxies_group = proxies_mutex_group.add_argument_group(
"Arguments that restrict exposed API proxies. One or both of these may "
"be specified if `--all-proxies` is not:")
proxies_group.add_argument(
"--apis",
metavar="API",
type=arg_parsers.ArgList(),
help="""\
Comma-separated names of API proxies to which this API product is bound. Only
those API proxies will be accessible through the new API product.
If not provided, all deployed API proxies will be included in the product, so
long as they match the other parameters.
The API proxy names must already be deployed to the bound environments, or
creation of the API product will fail. To get a list of deployed API proxies,
run:
$ {grandparent_command} deployments list
To deploy an API proxy, run:
$ {grandparent_command} apis deploy""")
# Resource paths conform to RFC 2396's segment format, so a "," may appear
# in one, but a "#" will not. " " or "|" would also have worked, but would
# have been harder to read in help text; spaces and pipes have special
# meaning in usage strings.
proxies_group.add_argument(
"--resources",
metavar="RESOURCE",
type=argument_groups.HashDelimitedArgList(min_length=1),
help="""\
API resources to be bundled in the API product, separated by `#` signs.
By default, the resource paths are mapped from the `proxy.pathsuffix` variable.
The proxy path suffix is defined as the URI fragment following the
ProxyEndpoint base path. For example, if ``/forecastrss'' is given as an element
of this list, and the base path defined for the API proxy is `/weather`, then
only requests to `/weather/forecastrss` are permitted by the API product.
Proxy paths can use asterisks as wildcards; `/**` indicates that all sub-URIs
are included, whereas a single asterisk indicates that only URIs one level
down are included.
By default, `/` supports the same resources as `/**` as well as the base path
defined by the API proxy.
For example, if the base path of the API proxy is `/v1/weatherapikey`, then
the API product supports requests to `/v1/weatherapikey` and to any sub-URIs,
such as `/v1/weatherapikey/forecastrss`, `/v1/weatherapikey/region/CA`, and so
on.
If not provided, all deployed API resources will be included in the product, so
long as they match the other parameters.
The API proxy resources must already be deployed to the bound environments, or
creation of the API product will fail.""")
quota_group = parser.add_argument_group(
("To impose a quota limit on calls to the API product, specify all of "
"the following:"))
quota_group.add_argument(
"--quota",
type=int,
help="""Number of request messages permitted per app by this API product
for the specified `--quota-interval` and `--quota-unit`.
For example, to create an API product that allows 50 requests every twelve hours
to every deployed API proxy, run:
$ {command} PRODUCT --all-environments --all-proxies --public-access --quota=50 --quota-interval=12 --quota-unit=hour
If specified, `--quota-interval` and `--quota-unit` must be specified too.""")
quota_group.add_argument(
"--quota-interval",
type=int,
help=("Time interval over which the number of request messages is "
"calculated.\n\n"
"If specified, `--quota` and `--quota-unit` must be specified "
"too."))
quota_group.add_argument(
"--quota-unit",
choices=["minute", "hour", "day", "month"],
help=("Time unit for `--quota-interval`.\n\n"
"If specified, `--quota` and `--quota-interval` must be "
"specified too."))
parser.add_argument(
"--description",
help=("Overview of the API product. Include key information about the "
"API product that is not captured by other fields."))
parser.add_argument(
"--manual-approval",
action="store_true",
help=("Require manual approval of developer requests to access this "
"API product before their consumer keys can be used. If unset, "
"the consumer key is generated in an \"approved\" state and can "
"be used immediately."))
parser.add_argument(
"--oauth-scopes",
metavar="SCOPE",
type=arg_parsers.ArgList(),
help=("Comma-separated list of OAuth scopes that are validated at "
"runtime. Apigee validates that the scopes in any access token "
"presented match the scopes defined in the OAuth policy "
"assoicated with the API product."))
parser.add_argument(
"--attributes",
metavar="NAME=VALUE",
type=arg_parsers.ArgDict(max_length=17),
help=("Key-value attribute pairs that may be used to extend the "
"default API product profile with customer-specific metadata. Up "
"to 17 attributes can be specified."))
def Run(self, args):
"""Run the deploy command."""
if args.organization is None:
args.organization = defaults.OrganizationFromGCPProject()
if console_io.CanPrompt():
if args.organization is None:
def _ListOrgs():
if properties.VALUES.api_endpoint_overrides.apigee.Get():
response = apigee.OrganizationsClient.List()
else:
response = apigee.OrganizationsClient.ListOrganizationsGlobal()
if "organizations" in response:
return [item["organization"] for item in response["organizations"]]
else:
return []
args.organization = prompts.ResourceFromFreeformPrompt(
"organization",
"the organization in which to create the API product", _ListOrgs)
if args.INTERNAL_NAME is None:
product_matcher = resource_args.ValidPatternForEntity("product")
valid_product = lambda name: product_matcher.search(name) is not None
args.INTERNAL_NAME = console_io.PromptWithValidator(
valid_product, "Empty or invalid API product name.",
"Enter an internal name for the new API product: ")
org_identifier = {"organizationsId": args.organization}
if args.environments is None:
list_envs = lambda: apigee.EnvironmentsClient.List(org_identifier)
choice = console_io.PromptChoice(
["Include all environments", "Choose environments to include"],
prompt_string=("What environments should be accessible in the API "
"product?"))
if choice == 0:
args.environments = []
else:
args.environments = prompts.ResourceListFromPrompt(
"environment", list_envs)
if not args.apis and not args.resources and not args.all_proxies:
choice = console_io.PromptChoice(
[
"Include all API proxies",
"Choose API proxies and/or basepaths to include"
],
prompt_string=("What API proxies should be accessible in the API "
"product?"))
if choice == 0:
args.all_proxies = True
else:
def _ListDeployedProxies():
response = apigee.DeploymentsClient.List(org_identifier)
return sorted(list(set(item["apiProxy"] for item in response)))
args.apis = prompts.ResourceListFromPrompt(
"api", _ListDeployedProxies, "Include all deployed API proxies")
resource_options = [
"Restrict proxy access by resource path",
"Include all resource paths of the product's API proxies"
]
if console_io.PromptChoice(resource_options) == 0:
args.resources = prompts.ListFromFreeformPrompt(
"Enter a resource path that should be included: ",
"Include another resource path",
"Include all resource paths of the product's API proxies")
else:
args.resources = []
if not args.resources and not args.apis:
# User explicitly chose to include all proxies and resources.
args.all_proxies = True
if args.access is None:
option = console_io.PromptChoice([
"Public - visible in the Apigee developer portal",
("Private - callable by external developers but not visible in the "
"Apigee developer portal"),
"Internal - not callable by external developers"
],
message="Choose an access policy.")
args.access = ["public", "private", "internal"][option]
if args.environments is None:
raise exceptions.OneOfArgumentsRequiredException(
["--environments", "--all-environments"],
"All API products must include at least one environment.")
if args.access is None:
raise exceptions.OneOfArgumentsRequiredException([
"--public-access", "--private-access", "--internal-access"
], "All API products must specify whether they can be publicly accessed.")
if not args.apis and not args.resources and not args.all_proxies:
raise exceptions.OneOfArgumentsRequiredException(
["--apis", "--resources", "--all-proxies"],
"All API products must include at least one API proxy or resource.")
quota_args_missing = [
arg for arg in vars(args) if "quota" in arg and vars(args)[arg] is None
]
if quota_args_missing:
if len(quota_args_missing) < 3:
raise exceptions.RequiredArgumentException(
"--" + quota_args_missing[0].replace("_", "-"),
"Must specify all quota arguments to use quotas.")
else:
args.quota = "%d" % args.quota
args.quota_interval = "%d" % args.quota_interval
attributes = [{"name": "access", "value": args.access}]
if args.attributes:
for key in args.attributes:
attributes.append({"name": key, "value": args.attributes[key]})
identifiers = args.CONCEPTS.internal_name.Parse().AsDict()
if args.display_name is None:
args.display_name = identifiers["apiproductsId"]
product = apigee.ProductsInfo(
name=identifiers["apiproductsId"],
displayName=args.display_name,
approvalType="manual" if args.manual_approval else "auto",
attributes=attributes,
description=args.description,
apiResources=args.resources if args.resources else None,
environments=args.environments if args.environments else None,
proxies=args.apis if args.apis else None,
quota=args.quota,
quotaInterval=args.quota_interval,
quotaTimeUnit=args.quota_unit,
scopes=args.oauth_scopes)
return apigee.ProductsClient.Create(identifiers, product)

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to delete an Apigee API product."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Delete(base.DescribeCommand):
"""Delete an Apigee API product."""
detailed_help = {
"EXAMPLES":
"""
To delete an API product called ``product-name'' from the active Cloud
Platform project, run:
$ {command} product-name
To delete an API product called ``other-product'' from an Apigee
organization called ``org-name'', run:
$ {command} other-product --organization=org-name
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.product",
"""\
API product to be deleted. To get a list of available API products, run:
$ {parent_command} list
""",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.product.Parse().AsDict()
return apigee.ProductsClient.Delete(identifiers)

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to describe an Apigee API product."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Describe(base.DescribeCommand):
"""Describe an Apigee API product."""
detailed_help = {
"EXAMPLES":
"""
To describe an API product called ``product-name'' in the active Cloud
Platform project, run:
$ {command} product-name
To describe an API product called ``other-product'' in an Apigee
organization called ``org-name'', run:
$ {command} other-product --organization=org-name
To describe an API product called ``product-name'' as a JSON object,
run:
$ {command} product-name --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser, "organization.product",
"API product to be described. To get a list of available API products, "
"run:\n\n\n"
" $ {parent_command} list\n\n",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
def Run(self, args):
"""Run the describe command."""
identifiers = args.CONCEPTS.product.Parse().AsDict()
return apigee.ProductsClient.Describe(identifiers)

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to list all API products in the relevant Apigee organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class List(base.ListCommand):
"""List Apigee API products."""
detailed_help = {
"DESCRIPTION":
"{description}",
"EXAMPLES":
"""
To list all API products for the active Cloud Platform project, run:
$ {command}
To get a JSON array of all the API products in an organization named
``my-org'', run:
$ {command} --organization=my-org --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization",
"Apigee organization whose products should be listed. If unspecified, "
"the Cloud Platform project's associated organization will be used.",
positional=False,
required=True,
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
parser.display_info.AddFormat("list(name)")
def Run(self, args):
"""Run the list command."""
identifiers = args.CONCEPTS.organization.Parse().AsDict()
return apigee.ProductsClient.List(identifiers)

View File

@@ -0,0 +1,413 @@
# -*- coding: utf-8 -*- # Lint as: python3
# Copyright 2020 Google Inc. 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.
"""Command to modify an Apigee API product."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import collections
from googlecloudsdk.api_lib import apigee
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.apigee import argument_groups
from googlecloudsdk.command_lib.apigee import defaults
from googlecloudsdk.command_lib.apigee import resource_args
class Update(base.DescribeCommand):
"""Update an existing Apigee API product."""
detailed_help = {
"DESCRIPTION":
"""
{description}
`{command}` applies a set of modifications to an existing API product.
To create a new API product, run:
$ {parent_command} create
""",
"EXAMPLES":
"""
To update the display name of the API product with the internal name ``my-prod'', run:
$ {command} my-prod --display-name="Example Project"
To update the description of the API product, run:
$ {command} my-prod --description="This API is famous for appearing in a YouTube video."
To remove the API product's description, run:
$ {command} my-prod --clear-description
To remove manual approval requirements from the API and change its access level to public, run:
$ {command} my-prod --public-access --automatic-approval
To impose a quota of 45 calls per minute per application on the API product, run:
$ {command} my-prod --quota=45 --quota-interval=1 --quota-unit=minute
To remove a quota on the API product and switch it to unlisted access with manual approval, run:
$ {command} my-prod --manual-approval --private-access --clear-quota
To set the API product's custom attribute ``foo'' to the value ``bar'', updating that attribute if it exists and creating it if it doesn't, and remove the attribute ``baz'' if it exists, run:
$ {command} my-prod --add-attribute="foo=bar" --remove-attribute=baz
To update the list of API proxies included in the API product, run:
$ {command} my-prod --add-api=NEW_ONE,NEW_TWO --remove-api=OLD_ONE,OLD_TWO
To switch the API product to including all ``test'' environment APIs no matter what API proxy or resource they expose, run:
$ {command} my-prod --add-environment=test --all-apis --all-resources
To update the list of API resources included in the API product, and
output the updated API product as a JSON object, run:
$ {command} my-prod --add-resource="NEW_ONE#NEW_TWO" --remove-resource="OLD_ONE#OLD_TWO" --format=json
"""
}
@staticmethod
def Args(parser):
resource_args.AddSingleResourceArgument(
parser,
"organization.product",
"API product to be updated. To get a list of available API products, "
"run:\n\n\n"
" $ {parent_command} list\n\n",
fallthroughs=[defaults.GCPProductOrganizationFallthrough()])
argument_groups.AddEditableListArgument(
parser,
"environment",
"environments",
"Environments to which the API product is bound. Requests to "
"environments that are not listed are rejected, preventing developers "
"from accessing those resources even if they can access the same API "
"proxies in another environment.\n\n"
"For example, this can be used to prevent applications with access to "
"production APIs from accessing the alpha or beta versions of those "
"APIs.\n\n"
"To get a list of available environments, run:\n\n"
" $ {grandparent_command} environments list",
clear_arg="--all-environments",
clear_help="Make all environments accessible through this API product.",
dest="environments")
parser.add_argument(
"--display-name",
dest="set_displayName",
help=("Name to be displayed in the UI or developer portal to "
"developers registering for API access."))
access_group = parser.add_mutually_exclusive_group()
access_group.add_argument(
"--public-access",
dest="access",
action="store_const",
const="public",
help=("Make this API product visible to developers in the Apigee "
"developer portal."))
access_group.add_argument(
"--private-access",
dest="access",
action="store_const",
const="private",
help=("Hide this API product in the developer portal but make it "
"accessible by external developers."))
access_group.add_argument(
"--internal-access",
dest="access",
action="store_const",
const="internal",
help="Prevent external access to this API product.")
argument_groups.AddEditableListArgument(
parser,
"API",
"APIs",
"""API proxies to which this API product is bound. Only those API
proxies will be accessible through the API product.
The API proxy names must already be deployed to the bound environments, or
creation of the API product will fail. To get a list of deployed API proxies,
run:
$ {grandparent_command} deployments list
To deploy an API proxy, run:
$ {grandparent_command} apis deploy.""",
dest="proxies",
clear_arg="--all-apis",
clear_help="Include all deployed API proxies in the product, so long "
"as they match the other parameters.")
argument_groups.AddEditableListArgument(
parser,
"resource",
"resources",
"""API resources to be bundled in the API product.
By default, the resource paths are mapped from the `proxy.pathsuffix` variable.
The proxy path suffix is defined as the URI fragment following the
ProxyEndpoint base path. For example, if ``/forecastrss'' is given as an element
of this list, and the base path defined for the API proxy is `/weather`, then
only requests to `/weather/forecastrss` are permitted by the API product.
Proxy paths can use asterisks as wildcards; `/**` indicates that all sub-URIs
are included, whereas a single asterisk indicates that only URIs one level
down are included.
By default, `/` supports the same resources as `/**` as well as the base path
defined by the API proxy.
For example, if the base path of the API proxy is `/v1/weatherapikey`, then
the API product supports requests to `/v1/weatherapikey` and to any sub-URIs,
such as `/v1/weatherapikey/forecastrss`, `/v1/weatherapikey/region/CA`, and so
on.
The API proxy resources must already be deployed to the bound environments, or
creation of the API product will fail.""",
dest="apiResources",
collector_type=argument_groups.HashDelimitedArgList(),
clear_arg="--all-resources",
clear_help="Include all deployed API resources in the product, so long "
"as they match the other parameters.")
quota_mutex = parser.add_mutually_exclusive_group()
quota_group = quota_mutex.add_argument_group(
("To impose a quota limit on calls to the API product, specify all of "
"the following:"))
quota_group.add_argument(
"--quota",
type=int,
help="""Number of request messages permitted per app by this API
product for the specified `--quota-interval` and `--quota-unit`.
For example, `--quota=50`, `--quota-interval=12`, and `--quota-unit=hour` means
50 requests are allowed every 12 hours.""")
quota_group.add_argument(
"--quota-interval",
type=int,
help=("Time interval over which the number of "
"request messages is calculated."))
quota_group.add_argument(
"--quota-unit",
choices=["minute", "hour", "day", "month"],
help=("Time unit for `--quota-interval`."))
quota_mutex.add_argument(
"--clear-quota",
action="store_true",
help="Remove any quota currently imposed on the API product.")
argument_groups.AddClearableArgument(
parser, "description",
"Overview of the API product. Include key information about the API "
"product that is not captured by other fields.",
"Remove the API product's description.")
approval_group = parser.add_mutually_exclusive_group()
approval_group.add_argument(
"--manual-approval",
action="store_const",
const="manual",
dest="set_approvalType",
help=("Require manual approval of developer requests to access this "
"API product before their consumer keys can be used."))
approval_group.add_argument(
"--automatic-approval",
action="store_const",
const="auto",
dest="set_approvalType",
help=("Allow developers to generate approved consumer keys without "
"waiting for approval."))
argument_groups.AddEditableListArgument(
parser,
"OAuth scope",
"OAuth scopes",
"Comma-separated list of OAuth scopes that are validated at runtime. "
"Apigee validates that the scopes in any access token presented match "
"the scopes defined in the OAuth policy assoicated with the API "
"product.",
dest="scopes")
argument_groups.AddEditableListArgument(
parser,
"attribute",
"attributes",
"Key-value attribute pairs that may be used to extend the default API "
"product profile with customer-specific metadata. Up to 17 attributes "
"can be specified.",
dict_like=True,
add_metavar="NAME=VALUE",
remove_metavar="NAME")
def Run(self, args):
"""Run the update command."""
identifiers = args.CONCEPTS.product.Parse().AsDict()
product = apigee.ProductsClient.Describe(identifiers)
## Quota related.
if args.quota is not None:
product["quota"] = "%d" % args.quota
if args.quota_interval is not None:
product["quotaInterval"] = "%d" % args.quota_interval
if args.quota_unit:
product["quotaTimeUnit"] = args.quota_unit
# Check that AFTER these updates, all three quota settings are present.
quota_field_names = ["quota", "quotaInterval", "quotaTimeUnit"]
quota_fields_exist = [field in product for field in quota_field_names]
if any(quota_fields_exist) and not all(quota_fields_exist):
if not args.quota_interval:
missing_arg = "--quota-interval"
elif not args.quota_unit:
missing_arg = "--quota-unit"
else:
missing_arg = "--quota"
raise exceptions.RequiredArgumentException(
missing_arg,
"Products with quotas must specify all three quota settings.")
if args.clear_quota:
del product["quota"]
del product["quotaInterval"]
del product["quotaTimeUnit"]
args.clear_quota = None
## Attribute list related
attribute_list = product["attributes"] if "attributes" in product else []
attribute_list = [(item["name"], item["value"]) for item in attribute_list]
attributes = collections.OrderedDict(attribute_list)
if args.add_attribute is not None:
add_attributes = args.add_attribute
if ("access" in add_attributes and
add_attributes["access"] not in ["public", "private", "internal"]):
raise exceptions.BadArgumentException(
"--add-attribute",
"The `access` attribute must be set to one of \"public\", "
"\"private\", or \"internal\".")
attributes.update(add_attributes)
args.add_attribute = None
if args.remove_attribute is not None:
for sublist in args.remove_attribute:
if "access" in sublist and not args.access:
raise exceptions.BadArgumentException(
"--remove-attribute", "The `access` attribute is required.")
for item in sublist:
if item in attributes:
del attributes[item]
args.remove_attribute = None
if args.clear_attributes:
# It doesn't make sense that the server would return an API product
# without access rules, but the API physically allows it, and an
# unexpected response mustn't cause gcloud to crash.
access = attributes["access"] if "access" in attributes else None
attributes = {"access": access} if access else {}
args.clear_attributes = None
if args.access:
attributes["access"] = args.access
attribute_dict = lambda item: {"name": item[0], "value": item[1]}
attributes_dicts = [attribute_dict(item) for item in attributes.items()]
product["attributes"] = attributes_dicts
# Python lint rules don't allow direct comparison with the empty string;
# detect it by process of elimination (not truthy, not None) instead.
if not args.set_displayName and args.set_displayName is not None:
raise exceptions.BadArgumentException(
"--display-name", "An API product's display name cannot be blank.")
# The rest of the fields can be filled in directly from arguments.
emptied_lists = set()
arg_dict = vars(args)
for key, value in arg_dict.items():
if value is None or "_" not in key:
continue
label, field = key.split("_", 1)
if label == "add":
if field not in product:
product[field] = []
for sublist in value:
product[field] += sublist
elif label == "remove" and field in product:
for sublist in value:
for item in sublist:
if item in product[field]:
product[field].remove(item)
if not product[field]:
# This removed the last item from `field`. None it out so it's
# not sent to the server in the update call.
product[field] = None
emptied_lists.add(field)
elif label == "set":
product[field] = value
elif label == "clear" and value and field in product:
del product[field]
# For API proxies, resources, and environments, don't allow the user to
# empty the list without explicitly stating that they intend to include ALL
# proxies/resources/environments. Otherwise the user may get results they
# didn't expect (removing a proxy -> the number of proxies exposed goes up).
if "proxies" in emptied_lists:
# User removed the last API proxy but didn't say to clear proxies. The
# result may not be what the user expected.
raise exceptions.BadArgumentException(
"--remove-api",
"An API product must include at least one API proxy, or else all "
"API proxies will implicitly be included. If this was intended, use "
"[--all-apis] instead of removing APIs individually.")
if "apiResources" in emptied_lists:
raise exceptions.BadArgumentException(
"--remove-resource",
"An API product must include at least one API resource, or else all "
"resources will implicitly be included. If this was intended, use "
"[--all-resources] instead of removing resources individually.")
if "environments" in emptied_lists:
raise exceptions.BadArgumentException(
"--remove-environment",
"An API product must include at least one environment, or else all "
"environments will implicitly be included. If this was intended, use "
"[--all-environments] instead of removing environments individually.")
# Clean up the product structure; remove any irrelevant fields that might
# have been populated by global gcloud args, and populate any empty fields
# with None.
product = {
key: (product[key] if key in product else None)
for key in apigee.ProductsInfo._fields
}
product["name"] = identifiers["apiproductsId"]
updated_product = apigee.ProductsInfo(**product)
return apigee.ProductsClient.Update(identifiers, updated_product)