1# Copyright (c) 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22 23import boto 24from boto.compat import json 25from boto.connection import AWSQueryConnection 26from boto.regioninfo import RegionInfo 27from boto.exception import JSONResponseError 28from boto.ec2containerservice import exceptions 29 30 31class EC2ContainerServiceConnection(AWSQueryConnection): 32 """ 33 Amazon EC2 Container Service (Amazon ECS) is a highly scalable, 34 fast, container management service that makes it easy to run, 35 stop, and manage Docker containers on a cluster of Amazon EC2 36 instances. Amazon ECS lets you launch and stop container-enabled 37 applications with simple API calls, allows you to get the state of 38 your cluster from a centralized service, and gives you access to 39 many familiar Amazon EC2 features like security groups, Amazon EBS 40 volumes, and IAM roles. 41 42 You can use Amazon ECS to schedule the placement of containers 43 across your cluster based on your resource needs, isolation 44 policies, and availability requirements. Amazon EC2 Container 45 Service eliminates the need for you to operate your own cluster 46 management and configuration management systems or worry about 47 scaling your management infrastructure. 48 """ 49 APIVersion = "2014-11-13" 50 DefaultRegionName = "us-east-1" 51 DefaultRegionEndpoint = "ecs.us-east-1.amazonaws.com" 52 ResponseError = JSONResponseError 53 54 _faults = { 55 "ServerException": exceptions.ServerException, 56 "ClientException": exceptions.ClientException, 57 } 58 59 60 def __init__(self, **kwargs): 61 region = kwargs.pop('region', None) 62 if not region: 63 region = RegionInfo(self, self.DefaultRegionName, 64 self.DefaultRegionEndpoint) 65 66 if 'host' not in kwargs or kwargs['host'] is None: 67 kwargs['host'] = region.endpoint 68 69 super(EC2ContainerServiceConnection, self).__init__(**kwargs) 70 self.region = region 71 72 def _required_auth_capability(self): 73 return ['hmac-v4'] 74 75 def create_cluster(self, cluster_name=None): 76 """ 77 Creates a new Amazon ECS cluster. By default, your account 78 will receive a `default` cluster when you launch your first 79 container instance. However, you can create your own cluster 80 with a unique name with the `CreateCluster` action. 81 82 During the preview, each account is limited to two clusters. 83 84 :type cluster_name: string 85 :param cluster_name: The name of your cluster. If you do not specify a 86 name for your cluster, you will create a cluster named `default`. 87 88 """ 89 params = {} 90 if cluster_name is not None: 91 params['clusterName'] = cluster_name 92 return self._make_request( 93 action='CreateCluster', 94 verb='POST', 95 path='/', params=params) 96 97 def delete_cluster(self, cluster): 98 """ 99 Deletes the specified cluster. You must deregister all 100 container instances from this cluster before you may delete 101 it. You can list the container instances in a cluster with 102 ListContainerInstances and deregister them with 103 DeregisterContainerInstance. 104 105 :type cluster: string 106 :param cluster: The cluster you want to delete. 107 108 """ 109 params = {'cluster': cluster, } 110 return self._make_request( 111 action='DeleteCluster', 112 verb='POST', 113 path='/', params=params) 114 115 def deregister_container_instance(self, container_instance, cluster=None, 116 force=None): 117 """ 118 Deregisters an Amazon ECS container instance from the 119 specified cluster. This instance will no longer be available 120 to run tasks. 121 122 :type cluster: string 123 :param cluster: The short name or full Amazon Resource Name (ARN) of 124 the cluster that hosts the container instance you want to 125 deregister. If you do not specify a cluster, the default cluster is 126 assumed. 127 128 :type container_instance: string 129 :param container_instance: The container instance UUID or full Amazon 130 Resource Name (ARN) of the container instance you want to 131 deregister. The ARN contains the `arn:aws:ecs` namespace, followed 132 by the region of the container instance, the AWS account ID of the 133 container instance owner, the `container-instance` namespace, and 134 then the container instance UUID. For example, arn:aws:ecs: region 135 : aws_account_id :container-instance/ container_instance_UUID . 136 137 :type force: boolean 138 :param force: Force the deregistration of the container instance. You 139 can use the `force` parameter if you have several tasks running on 140 a container instance and you don't want to run `StopTask` for each 141 task before deregistering the container instance. 142 143 """ 144 params = {'containerInstance': container_instance, } 145 if cluster is not None: 146 params['cluster'] = cluster 147 if force is not None: 148 params['force'] = str( 149 force).lower() 150 return self._make_request( 151 action='DeregisterContainerInstance', 152 verb='POST', 153 path='/', params=params) 154 155 def deregister_task_definition(self, task_definition): 156 """ 157 Deregisters the specified task definition. You will no longer 158 be able to run tasks from this definition after 159 deregistration. 160 161 :type task_definition: string 162 :param task_definition: The `family` and `revision` ( 163 `family:revision`) or full Amazon Resource Name (ARN) of the task 164 definition that you want to deregister. 165 166 """ 167 params = {'taskDefinition': task_definition, } 168 return self._make_request( 169 action='DeregisterTaskDefinition', 170 verb='POST', 171 path='/', params=params) 172 173 def describe_clusters(self, clusters=None): 174 """ 175 Describes one or more of your clusters. 176 177 :type clusters: list 178 :param clusters: A space-separated list of cluster names or full 179 cluster Amazon Resource Name (ARN) entries. If you do not specify a 180 cluster, the default cluster is assumed. 181 182 """ 183 params = {} 184 if clusters is not None: 185 self.build_list_params(params, 186 clusters, 187 'clusters.member') 188 return self._make_request( 189 action='DescribeClusters', 190 verb='POST', 191 path='/', params=params) 192 193 def describe_container_instances(self, container_instances, cluster=None): 194 """ 195 Describes Amazon EC2 Container Service container instances. 196 Returns metadata about registered and remaining resources on 197 each container instance requested. 198 199 :type cluster: string 200 :param cluster: The short name or full Amazon Resource Name (ARN) of 201 the cluster that hosts the container instances you want to 202 describe. If you do not specify a cluster, the default cluster is 203 assumed. 204 205 :type container_instances: list 206 :param container_instances: A space-separated list of container 207 instance UUIDs or full Amazon Resource Name (ARN) entries. 208 209 """ 210 params = {} 211 self.build_list_params(params, 212 container_instances, 213 'containerInstances.member') 214 if cluster is not None: 215 params['cluster'] = cluster 216 return self._make_request( 217 action='DescribeContainerInstances', 218 verb='POST', 219 path='/', params=params) 220 221 def describe_task_definition(self, task_definition): 222 """ 223 Describes a task definition. 224 225 :type task_definition: string 226 :param task_definition: The `family` and `revision` ( 227 `family:revision`) or full Amazon Resource Name (ARN) of the task 228 definition that you want to describe. 229 230 """ 231 params = {'taskDefinition': task_definition, } 232 return self._make_request( 233 action='DescribeTaskDefinition', 234 verb='POST', 235 path='/', params=params) 236 237 def describe_tasks(self, tasks, cluster=None): 238 """ 239 Describes a specified task or tasks. 240 241 :type cluster: string 242 :param cluster: The short name or full Amazon Resource Name (ARN) of 243 the cluster that hosts the task you want to describe. If you do not 244 specify a cluster, the default cluster is assumed. 245 246 :type tasks: list 247 :param tasks: A space-separated list of task UUIDs or full Amazon 248 Resource Name (ARN) entries. 249 250 """ 251 params = {} 252 self.build_list_params(params, 253 tasks, 254 'tasks.member') 255 if cluster is not None: 256 params['cluster'] = cluster 257 return self._make_request( 258 action='DescribeTasks', 259 verb='POST', 260 path='/', params=params) 261 262 def discover_poll_endpoint(self, container_instance=None): 263 """ 264 This action is only used by the Amazon EC2 Container Service 265 agent, and it is not intended for use outside of the agent. 266 267 268 Returns an endpoint for the Amazon EC2 Container Service agent 269 to poll for updates. 270 271 :type container_instance: string 272 :param container_instance: The container instance UUID or full Amazon 273 Resource Name (ARN) of the container instance. The ARN contains the 274 `arn:aws:ecs` namespace, followed by the region of the container 275 instance, the AWS account ID of the container instance owner, the 276 `container-instance` namespace, and then the container instance 277 UUID. For example, arn:aws:ecs: region : aws_account_id :container- 278 instance/ container_instance_UUID . 279 280 """ 281 params = {} 282 if container_instance is not None: 283 params['containerInstance'] = container_instance 284 return self._make_request( 285 action='DiscoverPollEndpoint', 286 verb='POST', 287 path='/', params=params) 288 289 def list_clusters(self, next_token=None, max_results=None): 290 """ 291 Returns a list of existing clusters. 292 293 :type next_token: string 294 :param next_token: The `nextToken` value returned from a previous 295 paginated `ListClusters` request where `maxResults` was used and 296 the results exceeded the value of that parameter. Pagination 297 continues from the end of the previous results that returned the 298 `nextToken` value. This value is `null` when there are no more 299 results to return. 300 301 :type max_results: integer 302 :param max_results: The maximum number of cluster results returned by 303 `ListClusters` in paginated output. When this parameter is used, 304 `ListClusters` only returns `maxResults` results in a single page 305 along with a `nextToken` response element. The remaining results of 306 the initial request can be seen by sending another `ListClusters` 307 request with the returned `nextToken` value. This value can be 308 between 1 and 100. If this parameter is not used, then 309 `ListClusters` returns up to 100 results and a `nextToken` value if 310 applicable. 311 312 """ 313 params = {} 314 if next_token is not None: 315 params['nextToken'] = next_token 316 if max_results is not None: 317 params['maxResults'] = max_results 318 return self._make_request( 319 action='ListClusters', 320 verb='POST', 321 path='/', params=params) 322 323 def list_container_instances(self, cluster=None, next_token=None, 324 max_results=None): 325 """ 326 Returns a list of container instances in a specified cluster. 327 328 :type cluster: string 329 :param cluster: The short name or full Amazon Resource Name (ARN) of 330 the cluster that hosts the container instances you want to list. If 331 you do not specify a cluster, the default cluster is assumed.. 332 333 :type next_token: string 334 :param next_token: The `nextToken` value returned from a previous 335 paginated `ListContainerInstances` request where `maxResults` was 336 used and the results exceeded the value of that parameter. 337 Pagination continues from the end of the previous results that 338 returned the `nextToken` value. This value is `null` when there are 339 no more results to return. 340 341 :type max_results: integer 342 :param max_results: The maximum number of container instance results 343 returned by `ListContainerInstances` in paginated output. When this 344 parameter is used, `ListContainerInstances` only returns 345 `maxResults` results in a single page along with a `nextToken` 346 response element. The remaining results of the initial request can 347 be seen by sending another `ListContainerInstances` request with 348 the returned `nextToken` value. This value can be between 1 and 349 100. If this parameter is not used, then `ListContainerInstances` 350 returns up to 100 results and a `nextToken` value if applicable. 351 352 """ 353 params = {} 354 if cluster is not None: 355 params['cluster'] = cluster 356 if next_token is not None: 357 params['nextToken'] = next_token 358 if max_results is not None: 359 params['maxResults'] = max_results 360 return self._make_request( 361 action='ListContainerInstances', 362 verb='POST', 363 path='/', params=params) 364 365 def list_task_definitions(self, family_prefix=None, next_token=None, 366 max_results=None): 367 """ 368 Returns a list of task definitions that are registered to your 369 account. You can filter the results by family name with the 370 `familyPrefix` parameter. 371 372 :type family_prefix: string 373 :param family_prefix: The name of the family that you want to filter 374 the `ListTaskDefinitions` results with. Specifying a `familyPrefix` 375 will limit the listed task definitions to definitions that belong 376 to that family. 377 378 :type next_token: string 379 :param next_token: The `nextToken` value returned from a previous 380 paginated `ListTaskDefinitions` request where `maxResults` was used 381 and the results exceeded the value of that parameter. Pagination 382 continues from the end of the previous results that returned the 383 `nextToken` value. This value is `null` when there are no more 384 results to return. 385 386 :type max_results: integer 387 :param max_results: The maximum number of task definition results 388 returned by `ListTaskDefinitions` in paginated output. When this 389 parameter is used, `ListTaskDefinitions` only returns `maxResults` 390 results in a single page along with a `nextToken` response element. 391 The remaining results of the initial request can be seen by sending 392 another `ListTaskDefinitions` request with the returned `nextToken` 393 value. This value can be between 1 and 100. If this parameter is 394 not used, then `ListTaskDefinitions` returns up to 100 results and 395 a `nextToken` value if applicable. 396 397 """ 398 params = {} 399 if family_prefix is not None: 400 params['familyPrefix'] = family_prefix 401 if next_token is not None: 402 params['nextToken'] = next_token 403 if max_results is not None: 404 params['maxResults'] = max_results 405 return self._make_request( 406 action='ListTaskDefinitions', 407 verb='POST', 408 path='/', params=params) 409 410 def list_tasks(self, cluster=None, container_instance=None, family=None, 411 next_token=None, max_results=None): 412 """ 413 Returns a list of tasks for a specified cluster. You can 414 filter the results by family name or by a particular container 415 instance with the `family` and `containerInstance` parameters. 416 417 :type cluster: string 418 :param cluster: The short name or full Amazon Resource Name (ARN) of 419 the cluster that hosts the tasks you want to list. If you do not 420 specify a cluster, the default cluster is assumed.. 421 422 :type container_instance: string 423 :param container_instance: The container instance UUID or full Amazon 424 Resource Name (ARN) of the container instance that you want to 425 filter the `ListTasks` results with. Specifying a 426 `containerInstance` will limit the results to tasks that belong to 427 that container instance. 428 429 :type family: string 430 :param family: The name of the family that you want to filter the 431 `ListTasks` results with. Specifying a `family` will limit the 432 results to tasks that belong to that family. 433 434 :type next_token: string 435 :param next_token: The `nextToken` value returned from a previous 436 paginated `ListTasks` request where `maxResults` was used and the 437 results exceeded the value of that parameter. Pagination continues 438 from the end of the previous results that returned the `nextToken` 439 value. This value is `null` when there are no more results to 440 return. 441 442 :type max_results: integer 443 :param max_results: The maximum number of task results returned by 444 `ListTasks` in paginated output. When this parameter is used, 445 `ListTasks` only returns `maxResults` results in a single page 446 along with a `nextToken` response element. The remaining results of 447 the initial request can be seen by sending another `ListTasks` 448 request with the returned `nextToken` value. This value can be 449 between 1 and 100. If this parameter is not used, then `ListTasks` 450 returns up to 100 results and a `nextToken` value if applicable. 451 452 """ 453 params = {} 454 if cluster is not None: 455 params['cluster'] = cluster 456 if container_instance is not None: 457 params['containerInstance'] = container_instance 458 if family is not None: 459 params['family'] = family 460 if next_token is not None: 461 params['nextToken'] = next_token 462 if max_results is not None: 463 params['maxResults'] = max_results 464 return self._make_request( 465 action='ListTasks', 466 verb='POST', 467 path='/', params=params) 468 469 def register_container_instance(self, cluster=None, 470 instance_identity_document=None, 471 instance_identity_document_signature=None, 472 total_resources=None): 473 """ 474 This action is only used by the Amazon EC2 Container Service 475 agent, and it is not intended for use outside of the agent. 476 477 478 Registers an Amazon EC2 instance into the specified cluster. 479 This instance will become available to place containers on. 480 481 :type cluster: string 482 :param cluster: The short name or full Amazon Resource Name (ARN) of 483 the cluster that you want to register your container instance with. 484 If you do not specify a cluster, the default cluster is assumed.. 485 486 :type instance_identity_document: string 487 :param instance_identity_document: 488 489 :type instance_identity_document_signature: string 490 :param instance_identity_document_signature: 491 492 :type total_resources: list 493 :param total_resources: 494 495 """ 496 params = {} 497 if cluster is not None: 498 params['cluster'] = cluster 499 if instance_identity_document is not None: 500 params['instanceIdentityDocument'] = instance_identity_document 501 if instance_identity_document_signature is not None: 502 params['instanceIdentityDocumentSignature'] = instance_identity_document_signature 503 if total_resources is not None: 504 self.build_complex_list_params( 505 params, total_resources, 506 'totalResources.member', 507 ('name', 'type', 'doubleValue', 'longValue', 'integerValue', 'stringSetValue')) 508 return self._make_request( 509 action='RegisterContainerInstance', 510 verb='POST', 511 path='/', params=params) 512 513 def register_task_definition(self, family, container_definitions): 514 """ 515 Registers a new task definition from the supplied `family` and 516 `containerDefinitions`. 517 518 :type family: string 519 :param family: You can specify a `family` for a task definition, which 520 allows you to track multiple versions of the same task definition. 521 You can think of the `family` as a name for your task definition. 522 523 :type container_definitions: list 524 :param container_definitions: A list of container definitions in JSON 525 format that describe the different containers that make up your 526 task. 527 528 """ 529 params = {'family': family, } 530 self.build_complex_list_params( 531 params, container_definitions, 532 'containerDefinitions.member', 533 ('name', 'image', 'cpu', 'memory', 'links', 'portMappings', 'essential', 'entryPoint', 'command', 'environment')) 534 return self._make_request( 535 action='RegisterTaskDefinition', 536 verb='POST', 537 path='/', params=params) 538 539 def run_task(self, task_definition, cluster=None, overrides=None, 540 count=None): 541 """ 542 Start a task using random placement and the default Amazon ECS 543 scheduler. If you want to use your own scheduler or place a 544 task on a specific container instance, use `StartTask` 545 instead. 546 547 :type cluster: string 548 :param cluster: The short name or full Amazon Resource Name (ARN) of 549 the cluster that you want to run your task on. If you do not 550 specify a cluster, the default cluster is assumed.. 551 552 :type task_definition: string 553 :param task_definition: The `family` and `revision` ( 554 `family:revision`) or full Amazon Resource Name (ARN) of the task 555 definition that you want to run. 556 557 :type overrides: dict 558 :param overrides: 559 560 :type count: integer 561 :param count: The number of instances of the specified task that you 562 would like to place on your cluster. 563 564 """ 565 params = {'taskDefinition': task_definition, } 566 if cluster is not None: 567 params['cluster'] = cluster 568 if overrides is not None: 569 params['overrides'] = overrides 570 if count is not None: 571 params['count'] = count 572 return self._make_request( 573 action='RunTask', 574 verb='POST', 575 path='/', params=params) 576 577 def start_task(self, task_definition, container_instances, cluster=None, 578 overrides=None): 579 """ 580 Starts a new task from the specified task definition on the 581 specified container instance or instances. If you want to use 582 the default Amazon ECS scheduler to place your task, use 583 `RunTask` instead. 584 585 :type cluster: string 586 :param cluster: The short name or full Amazon Resource Name (ARN) of 587 the cluster that you want to start your task on. If you do not 588 specify a cluster, the default cluster is assumed.. 589 590 :type task_definition: string 591 :param task_definition: The `family` and `revision` ( 592 `family:revision`) or full Amazon Resource Name (ARN) of the task 593 definition that you want to start. 594 595 :type overrides: dict 596 :param overrides: 597 598 :type container_instances: list 599 :param container_instances: The container instance UUIDs or full Amazon 600 Resource Name (ARN) entries for the container instances on which 601 you would like to place your task. 602 603 """ 604 params = {'taskDefinition': task_definition, } 605 self.build_list_params(params, 606 container_instances, 607 'containerInstances.member') 608 if cluster is not None: 609 params['cluster'] = cluster 610 if overrides is not None: 611 params['overrides'] = overrides 612 return self._make_request( 613 action='StartTask', 614 verb='POST', 615 path='/', params=params) 616 617 def stop_task(self, task, cluster=None): 618 """ 619 Stops a running task. 620 621 :type cluster: string 622 :param cluster: The short name or full Amazon Resource Name (ARN) of 623 the cluster that hosts the task you want to stop. If you do not 624 specify a cluster, the default cluster is assumed.. 625 626 :type task: string 627 :param task: The task UUIDs or full Amazon Resource Name (ARN) entry of 628 the task you would like to stop. 629 630 """ 631 params = {'task': task, } 632 if cluster is not None: 633 params['cluster'] = cluster 634 return self._make_request( 635 action='StopTask', 636 verb='POST', 637 path='/', params=params) 638 639 def submit_container_state_change(self, cluster=None, task=None, 640 container_name=None, status=None, 641 exit_code=None, reason=None, 642 network_bindings=None): 643 """ 644 This action is only used by the Amazon EC2 Container Service 645 agent, and it is not intended for use outside of the agent. 646 647 648 Sent to acknowledge that a container changed states. 649 650 :type cluster: string 651 :param cluster: The short name or full Amazon Resource Name (ARN) of 652 the cluster that hosts the container. 653 654 :type task: string 655 :param task: The task UUID or full Amazon Resource Name (ARN) of the 656 task that hosts the container. 657 658 :type container_name: string 659 :param container_name: The name of the container. 660 661 :type status: string 662 :param status: The status of the state change request. 663 664 :type exit_code: integer 665 :param exit_code: The exit code returned for the state change request. 666 667 :type reason: string 668 :param reason: The reason for the state change request. 669 670 :type network_bindings: list 671 :param network_bindings: The network bindings of the container. 672 673 """ 674 params = {} 675 if cluster is not None: 676 params['cluster'] = cluster 677 if task is not None: 678 params['task'] = task 679 if container_name is not None: 680 params['containerName'] = container_name 681 if status is not None: 682 params['status'] = status 683 if exit_code is not None: 684 params['exitCode'] = exit_code 685 if reason is not None: 686 params['reason'] = reason 687 if network_bindings is not None: 688 self.build_complex_list_params( 689 params, network_bindings, 690 'networkBindings.member', 691 ('bindIP', 'containerPort', 'hostPort')) 692 return self._make_request( 693 action='SubmitContainerStateChange', 694 verb='POST', 695 path='/', params=params) 696 697 def submit_task_state_change(self, cluster=None, task=None, status=None, 698 reason=None): 699 """ 700 This action is only used by the Amazon EC2 Container Service 701 agent, and it is not intended for use outside of the agent. 702 703 704 Sent to acknowledge that a task changed states. 705 706 :type cluster: string 707 :param cluster: The short name or full Amazon Resource Name (ARN) of 708 the cluster that hosts the task. 709 710 :type task: string 711 :param task: The task UUID or full Amazon Resource Name (ARN) of the 712 task in the state change request. 713 714 :type status: string 715 :param status: The status of the state change request. 716 717 :type reason: string 718 :param reason: The reason for the state change request. 719 720 """ 721 params = {} 722 if cluster is not None: 723 params['cluster'] = cluster 724 if task is not None: 725 params['task'] = task 726 if status is not None: 727 params['status'] = status 728 if reason is not None: 729 params['reason'] = reason 730 return self._make_request( 731 action='SubmitTaskStateChange', 732 verb='POST', 733 path='/', params=params) 734 735 def _make_request(self, action, verb, path, params): 736 params['ContentType'] = 'JSON' 737 response = self.make_request(action=action, verb='POST', 738 path='/', params=params) 739 body = response.read().decode('utf-8') 740 boto.log.debug(body) 741 if response.status == 200: 742 return json.loads(body) 743 else: 744 json_body = json.loads(body) 745 fault_name = json_body.get('Error', {}).get('Code', None) 746 exception_class = self._faults.get(fault_name, self.ResponseError) 747 raise exception_class(response.status, response.reason, 748 body=json_body) 749