From 70629c66525d102165e235675ec2cee3827b8bf3 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Fri, 6 Nov 2020 14:52:07 +0100 Subject: [PATCH] add CLI command to view system status; release v0.12.2 --- CHANGELOG.md | 1 + README.md | 1 + bin/Dockerfile.base | 3 ++ localstack/constants.py | 2 +- localstack/utils/bootstrap.py | 52 ++++++++++++++++++++++++++++++++--- localstack/utils/cli.py | 30 ++++++++++++++++++-- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf599c88d7ff..4e409d4d4e182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # LocalStack Change Log +* v0.12.2: Add proper CORS headers to S3 responses; expose SecretsManager secrets via SSM parameter references; fix parsing of Content-Disposition header in S3 multipart uploads; add carriage return to SQS message payload validation regex; add support for SQS queue names containing slashes; add support for resource policies in SecretsManager; add test for S3 precondition check; add test for Terraform API Gateway resources; use distinct values for missing CloudFormation resource props; handle non-zero ReceiveMessageWaitTimeSeconds for SQS; refactor CF tests; add support for custom CI build commands via $CUSTOM_CMD; add README instructions for test credentials required for presigned URLs; fix Content-Length mismatch issue for HTTP server responses; add util function to create API GW Lambda event request context; fix hypercorn response body on 412 status code; upgrade base image and local Lambda executor to Java 11; allow adding multiple permission statements to Lambda function policy; minor: fix logic for missing authorizerId in API GW authorizers * v0.12.1: Fix missing module import for Windows; set correct Id attribute in S3 notification messages; enable setting custom authorizers on API GW resource methods; add log output listener for shell command thread; fix DynamoDB streams sequence number mismatch on GetRecords; add docs about using AWS CLI v2 from Docker image; updates in developer documentation and README (e.g., docker-compose version); support filtering of events by pattern using eventPattern; add CloudFormation support for Lambda::Permission; fix date format in CloudWatch responses; fix service name and NS URLs for XML service responses; add Terraform test for several resources (Lambdas, S3 bucket CORS integration); fixed gzip encoding for empty response content; refactor StepFunctions integration tests; pass $EDGE_PORT to Lambdas; add test for creating SFN State Machine with Choice operator; add test for creating SQS queue with attributes via CloudFormation * v0.12.0: Single edge port now stable for all APIs; publish SNS messages asynchronously; fix edge route mapping for S3 HEAD requests; fix invalid account ID in CloudWatch logs listener ARNs; minor fix of XML root tag for S3 error responses; fix issue with blocking sockets in HTTP2 server; minor refactoring of /graph endpoint; fix numeric timestamp format in Kinesis records required by Go SDK; add CloudWatch as a static dependency for Lambda API; disable custom Java Lambda executor and replace with default lambci mechanism; add CBOR encoding support for Kinesis API; raise error when attempting to update CF stack with unmodified template; create env INIT_SCRIPTS_PATH to specify path for init files in Docker; add more fine-grained DynamoDB error injection with read/write error probabilities; extract InputPath expression for notification messages to EventBus targets; fix missing attributes when adding IAM permission to Lambda function; fix case sensitivity for bucket names in S3 bucket notifications; add simple integration test for Terraform resources; fix debug port parser for Java Lambda executor to support different formats; add signature and expiry validation for S3 presigned URLs * v0.11.6: !!Breaking Change!! Starting with this release, all services are now exposed via the edge service (port `4566`) only; fix boolean values in IAM responses; fix PutEvents API call with no source specified; add download URLs for Elasticsearch 7.7; minor refactoring in SFN test code; fix endless loop when S3 error document is missing; add integration test to verify MAP tasks in SFN state machines; refactor CF logic for API GW resources and empty/Null values; fix Python path when running Lambdas using local executor; refactor API Gateway utils and test code; refactor Lambda context handling; add LAMBDA_DOCKER_DNS config to utilise custom DNS server for Lambda containers; fix "TypeName" as optional param for Elasticsearch v7; refactor startup logs to print correct ports in output; extend Serverless tests with SQS and API Gateway resources; use S3 path addressing when host starts with localhost IP or hostname; add test to assert event deletion for SQS Lambda event source; fix error response for requests to non-activated APIs; add localhost.localstack.cloud as alias to SSL cert; fix network_mode:bridge in docker-compose.yml; add Serverless integration tests to cover DynamoDB and Kinesis resources; add switch to disable Lambda handler validation; add Lambda request body length check; use edge as single entry point; performance improvements in plugin loading; forward requests in-memory instead of opening port per service diff --git a/README.md b/README.md index ef222795ecd44..e0f3994a22553 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ In addition to the above, the [**Pro version** of LocalStack](https://localstack * **IoT** * **Kinesis Data Analytics** * **Lambda Layers** +* **Managed Streaming for Kafka (MSK)** * **MediaStore** * **QLDB** * **RDS** diff --git a/bin/Dockerfile.base b/bin/Dockerfile.base index fd857047a09e4..99071a973a736 100644 --- a/bin/Dockerfile.base +++ b/bin/Dockerfile.base @@ -118,3 +118,6 @@ RUN rm -rf localstack/infra/elasticsearch/jdk/ RUN rm -rf /root/.npm; \ apk del --purge autoconf automake boost-dev build-base g++ gcc giflib krb5-conf krb5-libs libc-dev \ libffi-dev libjpeg-turbo libmagic libpng linux-headers musl-dev openssl-dev python3-dev + +# final fixes +RUN which python || ln -s /usr/bin/python3 /usr/bin/python diff --git a/localstack/constants.py b/localstack/constants.py index 7a64507be8d7f..87140da02cfd8 100644 --- a/localstack/constants.py +++ b/localstack/constants.py @@ -2,7 +2,7 @@ import localstack_client.config # LocalStack version -VERSION = '0.12.1' +VERSION = '0.12.2' # constant to represent the "local" region, i.e., local machine REGION_LOCAL = 'local' diff --git a/localstack/utils/bootstrap.py b/localstack/utils/bootstrap.py index 034b6dba0a4bf..5314c687160a1 100644 --- a/localstack/utils/bootstrap.py +++ b/localstack/utils/bootstrap.py @@ -1,6 +1,7 @@ import os import re import sys +import json import time import select import pkgutil @@ -139,7 +140,8 @@ def load_plugins(scope=None): return PLUGINS_LOADED[scope] t1 = now_utc() - setup_logging() + log_level = logging.WARNING if scope == PLUGIN_SCOPE_COMMANDS else None + setup_logging(log_level=log_level) loaded_files = [] result = [] @@ -185,6 +187,25 @@ def docker_container_running(container_name): return container_name in container_names +def get_docker_image_details(image_name=None): + image_name = image_name or get_docker_image_to_start() + try: + result = run('%s inspect %s' % (config.DOCKER_CMD, image_name), print_error=False) + result = json.loads(to_str(result)) + assert len(result) + except Exception: + return {} + if len(result) > 1: + LOG.warning('Found multiple images (%s) named "%s"' % (len(result), image_name)) + result = result[0] + result = { + 'id': result['Id'].replace('sha256:', '')[:12], + 'tag': (result.get('RepoTags') or ['latest'])[0].split(':')[-1], + 'created': result['Created'].split('.')[0] + } + return result + + def get_docker_container_names(): cmd = "%s ps --format '{{.Names}}'" % config.DOCKER_CMD try: @@ -205,15 +226,38 @@ def get_main_container_ip(): def get_main_container_name(): cmd = "%s inspect -f '{{ .Name }}' %s" % (config.DOCKER_CMD, config.HOSTNAME) - return run(cmd).strip().lstrip('/') + try: + return run(cmd, print_error=False).strip().lstrip('/') + except Exception: + return config.MAIN_CONTAINER_NAME + + +def get_server_version(): + docker_cmd = config.DOCKER_CMD + try: + # try to extract from existing running container + container_name = get_main_container_name() + version = run('%s exec -it %s bin/localstack --version' % (docker_cmd, container_name), print_error=False) + version = version.strip().split('\n')[-1] + return version + except Exception: + try: + # try to extract by starting a new container + img_name = get_docker_image_to_start() + version = run('%s run --entrypoint= -it %s bin/localstack --version' % (docker_cmd, img_name)) + version = version.strip().split('\n')[-1] + return version + except Exception: + # fall back to default constant + return constants.VERSION -def setup_logging(): +def setup_logging(log_level=None): # determine and set log level if PLUGINS_LOADED.get('_logging_'): return PLUGINS_LOADED['_logging_'] = True - log_level = logging.DEBUG if is_debug() else logging.INFO + log_level = log_level or (logging.DEBUG if is_debug() else logging.INFO) logging.basicConfig(level=log_level, format=LOG_FORMAT, datefmt=LOG_DATE_FORMAT) # set up werkzeug logger diff --git a/localstack/utils/cli.py b/localstack/utils/cli.py index 0fd56ad9cd9ab..cd653535dcb1b 100644 --- a/localstack/utils/cli.py +++ b/localstack/utils/cli.py @@ -23,7 +23,8 @@ from localstack import config, constants from localstack.utils import bootstrap from localstack.utils.bootstrap import ( - start_infra_in_docker, start_infra_locally, run, docker_container_running) + start_infra_in_docker, start_infra_locally, run, docker_container_running, + get_main_container_ip, get_main_container_name, get_docker_image_details, get_server_version) # Note: make sure we don't have other imports at the root level here @@ -43,7 +44,6 @@ def cmd_infra(argv, args): --docker Run the infrastructure in a Docker container (default) --host Run the infrastructure on the local host """ - print_version() if argv[0] == 'start': argv = ['infra', 'start'] + argv[1:] args[''] = 'infra' @@ -57,6 +57,7 @@ def cmd_infra(argv, args): if in_docker: start_infra_in_docker() else: + print_version() start_infra_locally() @@ -102,6 +103,27 @@ def cmd_ssh(argv, args): pass +def cmd_status(argv, args): + """ +Usage: + localstack status + """ + args.update(docopt(cmd_status.__doc__.strip(), argv=argv)) + print_status() + + +def print_status(): + print('Base version:\t\t%s' % get_server_version()) + img = get_docker_image_details() + print('Docker image:\t\tTag %s, ID %s, Created %s' % (img['tag'], img['id'], img['created'])) + cont_name = config.MAIN_CONTAINER_NAME + running = docker_container_running(cont_name) + cont_status = 'stopped' + if running: + cont_status = 'running (name: "%s", IP: %s)' % (get_main_container_name(), get_main_container_ip()) + print('Container status:\t%s' % cont_status) + + def print_version(): print('LocalStack version: %s' % constants.VERSION) @@ -125,6 +147,10 @@ def main(): 'description': 'Shorthand to obtain a shell in the running container', 'function': cmd_ssh } + config.CLI_COMMANDS['status'] = { + 'description': 'Obtain status details about the installation', + 'function': cmd_status + } # load CLI plugins bootstrap.load_plugins(scope=bootstrap.PLUGIN_SCOPE_COMMANDS)