diff --git a/.changes/next-release/enhancement-gamelift-57992.json b/.changes/next-release/enhancement-gamelift-57992.json new file mode 100644 index 000000000000..936c56e07108 --- /dev/null +++ b/.changes/next-release/enhancement-gamelift-57992.json @@ -0,0 +1,5 @@ +{ + "type": "enhancement", + "category": "``gamelift``", + "description": "Add ``--tags`` parameter to ``upload-build`` command." +} diff --git a/awscli/customizations/gamelift/uploadbuild.py b/awscli/customizations/gamelift/uploadbuild.py index f8cbd2a457a1..00d2339e30e5 100644 --- a/awscli/customizations/gamelift/uploadbuild.py +++ b/awscli/customizations/gamelift/uploadbuild.py @@ -23,6 +23,19 @@ from awscli.customizations.s3.utils import human_readable_size +def parse_tags(raw_tags_list): + """Parse tags from Key=Value format to GameLift API format.""" + tags_list = [] + if raw_tags_list: + for tag in raw_tags_list: + if '=' in tag: + key, value = tag.split('=', 1) + else: + key, value = tag, '' + tags_list.append({'Key': key, 'Value': value}) + return tags_list + + class UploadBuildCommand(BasicCommand): NAME = 'upload-build' DESCRIPTION = 'Upload a new build to AWS GameLift.' @@ -53,6 +66,12 @@ class UploadBuildCommand(BasicCommand): 'required': False, 'help_text': 'The operating system the build runs on', }, + { + 'name': 'tags', + 'required': False, + 'nargs': '+', + 'help_text': 'Tags to assign to the build. Format: Key=Value', + }, ] def _run_main(self, args, parsed_globals): @@ -80,6 +99,8 @@ def _run_main(self, args, parsed_globals): create_build_kwargs['OperatingSystem'] = args.operating_system if args.server_sdk_version: create_build_kwargs['ServerSdkVersion'] = args.server_sdk_version + if args.tags: + create_build_kwargs['Tags'] = parse_tags(args.tags) response = gamelift_client.create_build(**create_build_kwargs) build_id = response['Build']['BuildId'] diff --git a/tests/functional/gamelift/test_upload_build.py b/tests/functional/gamelift/test_upload_build.py index 6d82650810fc..46f5132680eb 100644 --- a/tests/functional/gamelift/test_upload_build.py +++ b/tests/functional/gamelift/test_upload_build.py @@ -264,3 +264,60 @@ def test_upload_build_with_server_sdk_version_param(self): stdout, ) self.assertIn('Build ID: myid', stdout) + + def test_upload_build_with_tags_param(self): + self.files.create_file('tmpfile', 'Some contents') + + expected_tags = [ + {'Key': 'Key1', 'Value': 'Value1'}, + {'Key': 'Key2', 'Value': 'Value2'} + ] + + cmdline = self.prefix + cmdline += ' --name mybuild --build-version myversion' + cmdline += ' --build-root %s' % self.files.rootdir + cmdline += ' --tags' + for tag in expected_tags: + cmdline += ' %s=%s' % (tag['Key'], tag['Value']) + + self.parsed_responses = [ + {'Build': {'BuildId': 'myid'}}, + {'StorageLocation': { + 'Bucket': 'mybucket', + 'Key': 'mykey'}, + 'UploadCredentials': { + 'AccessKeyId': 'myaccesskey', + 'SecretAccessKey': 'mysecretkey', + 'SessionToken': 'mytoken'}}, + {} + ] + + stdout, stderr, rc = self.run_cmd(cmdline, expected_rc=0) + + # First the build is created. + self.assertEqual(len(self.operations_called), 3) + self.assertEqual(self.operations_called[0][0].name, 'CreateBuild') + self.assertEqual( + self.operations_called[0][1], + {'Name': 'mybuild', 'Version': 'myversion', + 'Tags': expected_tags} + ) + + # Second the credentials are requested. + self.assertEqual( + self.operations_called[1][0].name, 'RequestUploadCredentials') + self.assertEqual( + self.operations_called[1][1], {'BuildId': 'myid'}) + + # The build is then uploaded to S3. + self.assertEqual(self.operations_called[2][0].name, 'PutObject') + self.assertEqual( + self.operations_called[2][1], + {'Body': mock.ANY, 'Bucket': 'mybucket', 'Key': 'mykey'} + ) + + # Check the output of the command. + self.assertIn( + 'Successfully uploaded %s to AWS GameLift' % self.files.rootdir, + stdout) + self.assertIn('Build ID: myid', stdout) diff --git a/tests/unit/customizations/gamelift/test_uploadbuild.py b/tests/unit/customizations/gamelift/test_uploadbuild.py index 9a1e5449266b..a912d6f853b5 100644 --- a/tests/unit/customizations/gamelift/test_uploadbuild.py +++ b/tests/unit/customizations/gamelift/test_uploadbuild.py @@ -20,6 +20,7 @@ from awscli.compat import StringIO from awscli.customizations.gamelift.uploadbuild import ( + parse_tags, UploadBuildCommand, validate_directory, zip_directory, @@ -246,6 +247,51 @@ def test_upload_build_when_server_sdk_version_is_provided(self): ServerSdkVersion=server_sdk_version, ) + def test_upload_build_when_tags_are_provided(self): + self.file_creator.create_file('tmpfile', 'Some contents') + self.args = [ + '--name', self.build_name, '--build-version', self.build_version, + '--build-root', self.build_root, + '--tags', 'Environment=Production', 'Team=GameDev' + ] + self.cmd(self.args, self.global_args) + + self.gamelift_client.create_build.assert_called_once_with( + Name=self.build_name, Version=self.build_version, + Tags=[ + {'Key': 'Environment', 'Value': 'Production'}, + {'Key': 'Team', 'Value': 'GameDev'} + ]) + + +class TestParseTags(unittest.TestCase): + def test_parse_tags_with_key_value_pairs(self): + result = parse_tags(['Key1=Value1', 'Key2=Value2']) + self.assertEqual(result, [ + {'Key': 'Key1', 'Value': 'Value1'}, + {'Key': 'Key2', 'Value': 'Value2'} + ]) + + def test_parse_tags_with_empty_value(self): + result = parse_tags(['Key1=']) + self.assertEqual(result, [{'Key': 'Key1', 'Value': ''}]) + + def test_parse_tags_without_equals(self): + result = parse_tags(['Key1']) + self.assertEqual(result, [{'Key': 'Key1', 'Value': ''}]) + + def test_parse_tags_with_equals_in_value(self): + result = parse_tags(['Key1=Value=WithEquals']) + self.assertEqual(result, [{'Key': 'Key1', 'Value': 'Value=WithEquals'}]) + + def test_parse_tags_with_none(self): + result = parse_tags(None) + self.assertEqual(result, []) + + def test_parse_tags_with_empty_list(self): + result = parse_tags([]) + self.assertEqual(result, []) + class TestZipDirectory(unittest.TestCase): def setUp(self):