1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2017 Google
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6# ----------------------------------------------------------------------------
7#
8#     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
9#
10# ----------------------------------------------------------------------------
11#
12#     This file is automatically generated by Magic Modules and manual
13#     changes will be clobbered when the file is regenerated.
14#
15#     Please read more about how to change this file at
16#     https://www.github.com/GoogleCloudPlatform/magic-modules
17#
18# ----------------------------------------------------------------------------
19
20from __future__ import absolute_import, division, print_function
21
22__metaclass__ = type
23
24################################################################################
25# Documentation
26################################################################################
27
28ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ["preview"], 'supported_by': 'community'}
29
30DOCUMENTATION = '''
31---
32module: gcp_container_node_pool
33description:
34- NodePool contains the name and configuration for a cluster's node pool.
35- Node pools are a set of nodes (i.e. VM's), with a common configuration and specification,
36  under the control of the cluster master. They may have a set of Kubernetes labels
37  applied to them, which may be used to reference them during pod scheduling. They
38  may also be resized up or down, to accommodate the workload.
39short_description: Creates a GCP NodePool
40version_added: 2.6
41author: Google Inc. (@googlecloudplatform)
42requirements:
43- python >= 2.6
44- requests >= 2.18.4
45- google-auth >= 1.3.0
46options:
47  state:
48    description:
49    - Whether the given object should exist in GCP
50    choices:
51    - present
52    - absent
53    default: present
54    type: str
55  name:
56    description:
57    - The name of the node pool.
58    required: false
59    type: str
60  config:
61    description:
62    - The node configuration of the pool.
63    required: false
64    type: dict
65    suboptions:
66      machine_type:
67        description:
68        - The name of a Google Compute Engine machine type (e.g.
69        - n1-standard-1). If unspecified, the default machine type is n1-standard-1.
70        required: false
71        type: str
72      disk_size_gb:
73        description:
74        - Size of the disk attached to each node, specified in GB. The smallest allowed
75          disk size is 10GB. If unspecified, the default disk size is 100GB.
76        required: false
77        type: int
78      oauth_scopes:
79        description:
80        - The set of Google API scopes to be made available on all of the node VMs
81          under the "default" service account.
82        - 'The following scopes are recommended, but not required, and by default
83          are not included: U(https://www.googleapis.com/auth/compute) is required
84          for mounting persistent storage on your nodes.'
85        - U(https://www.googleapis.com/auth/devstorage.read_only) is required for
86          communicating with gcr.io (the Google Container Registry).
87        - If unspecified, no scopes are added, unless Cloud Logging or Cloud Monitoring
88          are enabled, in which case their required scopes will be added.
89        required: false
90        type: list
91      service_account:
92        description:
93        - The Google Cloud Platform Service Account to be used by the node VMs. If
94          no Service Account is specified, the "default" service account is used.
95        required: false
96        type: str
97      metadata:
98        description:
99        - The metadata key/value pairs assigned to instances in the cluster.
100        - 'Keys must conform to the regexp [a-zA-Z0-9-_]+ and be less than 128 bytes
101          in length. These are reflected as part of a URL in the metadata server.
102          Additionally, to avoid ambiguity, keys must not conflict with any other
103          metadata keys for the project or be one of the four reserved keys: "instance-template",
104          "kube-env", "startup-script", and "user-data" Values are free-form strings,
105          and only have meaning as interpreted by the image running in the instance.
106          The only restriction placed on them is that each value''s size must be less
107          than or equal to 32 KB.'
108        - The total size of all keys and values must be less than 512 KB.
109        - 'An object containing a list of "key": value pairs.'
110        - 'Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.'
111        required: false
112        type: dict
113      image_type:
114        description:
115        - The image type to use for this node. Note that for a given image type, the
116          latest version of it will be used.
117        required: false
118        type: str
119      labels:
120        description:
121        - 'The map of Kubernetes labels (key/value pairs) to be applied to each node.
122          These will added in addition to any default label(s) that Kubernetes may
123          apply to the node. In case of conflict in label keys, the applied set may
124          differ depending on the Kubernetes version -- it''s best to assume the behavior
125          is undefined and conflicts should be avoided. For more information, including
126          usage and the valid values, see: U(http://kubernetes.io/v1.1/docs/user-guide/labels.html)
127          An object containing a list of "key": value pairs.'
128        - 'Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.'
129        required: false
130        type: dict
131      local_ssd_count:
132        description:
133        - The number of local SSD disks to be attached to the node.
134        - 'The limit for this value is dependant upon the maximum number of disks
135          available on a machine per zone. See: U(https://cloud.google.com/compute/docs/disks/local-ssd#local_ssd_limits)
136          for more information.'
137        required: false
138        type: int
139      tags:
140        description:
141        - The list of instance tags applied to all nodes. Tags are used to identify
142          valid sources or targets for network firewalls and are specified by the
143          client during cluster or node pool creation. Each tag within the list must
144          comply with RFC1035.
145        required: false
146        type: list
147      preemptible:
148        description:
149        - 'Whether the nodes are created as preemptible VM instances. See: U(https://cloud.google.com/compute/docs/instances/preemptible)
150          for more information about preemptible VM instances.'
151        required: false
152        type: bool
153      accelerators:
154        description:
155        - A list of hardware accelerators to be attached to each node.
156        required: false
157        type: list
158        version_added: 2.9
159        suboptions:
160          accelerator_count:
161            description:
162            - The number of the accelerator cards exposed to an instance.
163            required: false
164            type: int
165          accelerator_type:
166            description:
167            - The accelerator type resource name.
168            required: false
169            type: str
170      disk_type:
171        description:
172        - Type of the disk attached to each node (e.g. 'pd-standard' or 'pd-ssd')
173          If unspecified, the default disk type is 'pd-standard' .
174        required: false
175        type: str
176        version_added: 2.9
177      min_cpu_platform:
178        description:
179        - Minimum CPU platform to be used by this instance. The instance may be scheduled
180          on the specified or newer CPU platform .
181        required: false
182        type: str
183        version_added: 2.9
184      taints:
185        description:
186        - List of kubernetes taints to be applied to each node.
187        required: false
188        type: list
189        version_added: 2.9
190        suboptions:
191          key:
192            description:
193            - Key for taint.
194            required: false
195            type: str
196          value:
197            description:
198            - Value for taint.
199            required: false
200            type: str
201          effect:
202            description:
203            - Effect for taint.
204            required: false
205            type: str
206  initial_node_count:
207    description:
208    - The initial node count for the pool. You must ensure that your Compute Engine
209      resource quota is sufficient for this number of instances. You must also have
210      available firewall and routes quota.
211    required: true
212    type: int
213  version:
214    description:
215    - The version of the Kubernetes of this node.
216    required: false
217    type: str
218    version_added: 2.8
219  autoscaling:
220    description:
221    - Autoscaler configuration for this NodePool. Autoscaler is enabled only if a
222      valid configuration is present.
223    required: false
224    type: dict
225    suboptions:
226      enabled:
227        description:
228        - Is autoscaling enabled for this node pool.
229        required: false
230        type: bool
231      min_node_count:
232        description:
233        - Minimum number of nodes in the NodePool. Must be >= 1 and <= maxNodeCount.
234        required: false
235        type: int
236      max_node_count:
237        description:
238        - Maximum number of nodes in the NodePool. Must be >= minNodeCount.
239        - There has to enough quota to scale up the cluster.
240        required: false
241        type: int
242  management:
243    description:
244    - Management configuration for this NodePool.
245    required: false
246    type: dict
247    suboptions:
248      auto_upgrade:
249        description:
250        - A flag that specifies whether node auto-upgrade is enabled for the node
251          pool. If enabled, node auto-upgrade helps keep the nodes in your node pool
252          up to date with the latest release version of Kubernetes.
253        required: false
254        type: bool
255      auto_repair:
256        description:
257        - A flag that specifies whether the node auto-repair is enabled for the node
258          pool. If enabled, the nodes in this node pool will be monitored and, if
259          they fail health checks too many times, an automatic repair action will
260          be triggered.
261        required: false
262        type: bool
263      upgrade_options:
264        description:
265        - Specifies the Auto Upgrade knobs for the node pool.
266        required: false
267        type: dict
268        suboptions: {}
269  max_pods_constraint:
270    description:
271    - The constraint on the maximum number of pods that can be run simultaneously
272      on a node in the node pool.
273    required: false
274    type: dict
275    version_added: 2.9
276    suboptions:
277      max_pods_per_node:
278        description:
279        - Constraint enforced on the max num of pods per node.
280        required: false
281        type: int
282  conditions:
283    description:
284    - Which conditions caused the current node pool state.
285    required: false
286    type: list
287    version_added: 2.9
288    suboptions:
289      code:
290        description:
291        - Machine-friendly representation of the condition.
292        - 'Some valid choices include: "UNKNOWN", "GCE_STOCKOUT", "GKE_SERVICE_ACCOUNT_DELETED",
293          "GCE_QUOTA_EXCEEDED", "SET_BY_OPERATOR"'
294        required: false
295        type: str
296  cluster:
297    description:
298    - The cluster this node pool belongs to.
299    - 'This field represents a link to a Cluster resource in GCP. It can be specified
300      in two ways. First, you can place a dictionary with key ''name'' and value of
301      your resource''s name Alternatively, you can add `register: name-of-resource`
302      to a gcp_container_cluster task and then set this cluster field to "{{ name-of-resource
303      }}"'
304    required: true
305    type: dict
306  location:
307    description:
308    - The location where the node pool is deployed.
309    required: true
310    type: str
311    aliases:
312    - region
313    - zone
314    version_added: 2.8
315extends_documentation_fragment: gcp
316'''
317
318EXAMPLES = '''
319- name: create a cluster
320  gcp_container_cluster:
321    name: cluster-nodepool
322    initial_node_count: 4
323    location: us-central1-a
324    project: "{{ gcp_project }}"
325    auth_kind: "{{ gcp_cred_kind }}"
326    service_account_file: "{{ gcp_cred_file }}"
327    state: present
328  register: cluster
329
330- name: create a node pool
331  gcp_container_node_pool:
332    name: my-pool
333    initial_node_count: 4
334    cluster: "{{ cluster }}"
335    location: us-central1-a
336    project: test_project
337    auth_kind: serviceaccount
338    service_account_file: "/tmp/auth.pem"
339    state: present
340'''
341
342RETURN = '''
343name:
344  description:
345  - The name of the node pool.
346  returned: success
347  type: str
348config:
349  description:
350  - The node configuration of the pool.
351  returned: success
352  type: complex
353  contains:
354    machineType:
355      description:
356      - The name of a Google Compute Engine machine type (e.g.
357      - n1-standard-1). If unspecified, the default machine type is n1-standard-1.
358      returned: success
359      type: str
360    diskSizeGb:
361      description:
362      - Size of the disk attached to each node, specified in GB. The smallest allowed
363        disk size is 10GB. If unspecified, the default disk size is 100GB.
364      returned: success
365      type: int
366    oauthScopes:
367      description:
368      - The set of Google API scopes to be made available on all of the node VMs under
369        the "default" service account.
370      - 'The following scopes are recommended, but not required, and by default are
371        not included: U(https://www.googleapis.com/auth/compute) is required for mounting
372        persistent storage on your nodes.'
373      - U(https://www.googleapis.com/auth/devstorage.read_only) is required for communicating
374        with gcr.io (the Google Container Registry).
375      - If unspecified, no scopes are added, unless Cloud Logging or Cloud Monitoring
376        are enabled, in which case their required scopes will be added.
377      returned: success
378      type: list
379    serviceAccount:
380      description:
381      - The Google Cloud Platform Service Account to be used by the node VMs. If no
382        Service Account is specified, the "default" service account is used.
383      returned: success
384      type: str
385    metadata:
386      description:
387      - The metadata key/value pairs assigned to instances in the cluster.
388      - 'Keys must conform to the regexp [a-zA-Z0-9-_]+ and be less than 128 bytes
389        in length. These are reflected as part of a URL in the metadata server. Additionally,
390        to avoid ambiguity, keys must not conflict with any other metadata keys for
391        the project or be one of the four reserved keys: "instance-template", "kube-env",
392        "startup-script", and "user-data" Values are free-form strings, and only have
393        meaning as interpreted by the image running in the instance. The only restriction
394        placed on them is that each value''s size must be less than or equal to 32
395        KB.'
396      - The total size of all keys and values must be less than 512 KB.
397      - 'An object containing a list of "key": value pairs.'
398      - 'Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.'
399      returned: success
400      type: dict
401    imageType:
402      description:
403      - The image type to use for this node. Note that for a given image type, the
404        latest version of it will be used.
405      returned: success
406      type: str
407    labels:
408      description:
409      - 'The map of Kubernetes labels (key/value pairs) to be applied to each node.
410        These will added in addition to any default label(s) that Kubernetes may apply
411        to the node. In case of conflict in label keys, the applied set may differ
412        depending on the Kubernetes version -- it''s best to assume the behavior is
413        undefined and conflicts should be avoided. For more information, including
414        usage and the valid values, see: U(http://kubernetes.io/v1.1/docs/user-guide/labels.html)
415        An object containing a list of "key": value pairs.'
416      - 'Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.'
417      returned: success
418      type: dict
419    localSsdCount:
420      description:
421      - The number of local SSD disks to be attached to the node.
422      - 'The limit for this value is dependant upon the maximum number of disks available
423        on a machine per zone. See: U(https://cloud.google.com/compute/docs/disks/local-ssd#local_ssd_limits)
424        for more information.'
425      returned: success
426      type: int
427    tags:
428      description:
429      - The list of instance tags applied to all nodes. Tags are used to identify
430        valid sources or targets for network firewalls and are specified by the client
431        during cluster or node pool creation. Each tag within the list must comply
432        with RFC1035.
433      returned: success
434      type: list
435    preemptible:
436      description:
437      - 'Whether the nodes are created as preemptible VM instances. See: U(https://cloud.google.com/compute/docs/instances/preemptible)
438        for more information about preemptible VM instances.'
439      returned: success
440      type: bool
441    accelerators:
442      description:
443      - A list of hardware accelerators to be attached to each node.
444      returned: success
445      type: complex
446      contains:
447        acceleratorCount:
448          description:
449          - The number of the accelerator cards exposed to an instance.
450          returned: success
451          type: int
452        acceleratorType:
453          description:
454          - The accelerator type resource name.
455          returned: success
456          type: str
457    diskType:
458      description:
459      - Type of the disk attached to each node (e.g. 'pd-standard' or 'pd-ssd') If
460        unspecified, the default disk type is 'pd-standard' .
461      returned: success
462      type: str
463    minCpuPlatform:
464      description:
465      - Minimum CPU platform to be used by this instance. The instance may be scheduled
466        on the specified or newer CPU platform .
467      returned: success
468      type: str
469    taints:
470      description:
471      - List of kubernetes taints to be applied to each node.
472      returned: success
473      type: complex
474      contains:
475        key:
476          description:
477          - Key for taint.
478          returned: success
479          type: str
480        value:
481          description:
482          - Value for taint.
483          returned: success
484          type: str
485        effect:
486          description:
487          - Effect for taint.
488          returned: success
489          type: str
490initialNodeCount:
491  description:
492  - The initial node count for the pool. You must ensure that your Compute Engine
493    resource quota is sufficient for this number of instances. You must also have
494    available firewall and routes quota.
495  returned: success
496  type: int
497status:
498  description:
499  - Status of nodes in this pool instance.
500  returned: success
501  type: str
502statusMessage:
503  description:
504  - Additional information about the current status of this node pool instance.
505  returned: success
506  type: str
507version:
508  description:
509  - The version of the Kubernetes of this node.
510  returned: success
511  type: str
512autoscaling:
513  description:
514  - Autoscaler configuration for this NodePool. Autoscaler is enabled only if a valid
515    configuration is present.
516  returned: success
517  type: complex
518  contains:
519    enabled:
520      description:
521      - Is autoscaling enabled for this node pool.
522      returned: success
523      type: bool
524    minNodeCount:
525      description:
526      - Minimum number of nodes in the NodePool. Must be >= 1 and <= maxNodeCount.
527      returned: success
528      type: int
529    maxNodeCount:
530      description:
531      - Maximum number of nodes in the NodePool. Must be >= minNodeCount.
532      - There has to enough quota to scale up the cluster.
533      returned: success
534      type: int
535management:
536  description:
537  - Management configuration for this NodePool.
538  returned: success
539  type: complex
540  contains:
541    autoUpgrade:
542      description:
543      - A flag that specifies whether node auto-upgrade is enabled for the node pool.
544        If enabled, node auto-upgrade helps keep the nodes in your node pool up to
545        date with the latest release version of Kubernetes.
546      returned: success
547      type: bool
548    autoRepair:
549      description:
550      - A flag that specifies whether the node auto-repair is enabled for the node
551        pool. If enabled, the nodes in this node pool will be monitored and, if they
552        fail health checks too many times, an automatic repair action will be triggered.
553      returned: success
554      type: bool
555    upgradeOptions:
556      description:
557      - Specifies the Auto Upgrade knobs for the node pool.
558      returned: success
559      type: complex
560      contains:
561        autoUpgradeStartTime:
562          description:
563          - This field is set when upgrades are about to commence with the approximate
564            start time for the upgrades, in RFC3339 text format.
565          returned: success
566          type: str
567        description:
568          description:
569          - This field is set when upgrades are about to commence with the description
570            of the upgrade.
571          returned: success
572          type: str
573maxPodsConstraint:
574  description:
575  - The constraint on the maximum number of pods that can be run simultaneously on
576    a node in the node pool.
577  returned: success
578  type: complex
579  contains:
580    maxPodsPerNode:
581      description:
582      - Constraint enforced on the max num of pods per node.
583      returned: success
584      type: int
585conditions:
586  description:
587  - Which conditions caused the current node pool state.
588  returned: success
589  type: complex
590  contains:
591    code:
592      description:
593      - Machine-friendly representation of the condition.
594      returned: success
595      type: str
596podIpv4CidrSize:
597  description:
598  - The pod CIDR block size per node in this node pool.
599  returned: success
600  type: int
601cluster:
602  description:
603  - The cluster this node pool belongs to.
604  returned: success
605  type: dict
606location:
607  description:
608  - The location where the node pool is deployed.
609  returned: success
610  type: str
611'''
612
613################################################################################
614# Imports
615################################################################################
616
617from ansible.module_utils.gcp_utils import navigate_hash, GcpSession, GcpModule, GcpRequest, remove_nones_from_dict, replace_resource_dict
618import json
619import time
620
621################################################################################
622# Main
623################################################################################
624
625
626def main():
627    """Main function"""
628
629    module = GcpModule(
630        argument_spec=dict(
631            state=dict(default='present', choices=['present', 'absent'], type='str'),
632            name=dict(type='str'),
633            config=dict(
634                type='dict',
635                options=dict(
636                    machine_type=dict(type='str'),
637                    disk_size_gb=dict(type='int'),
638                    oauth_scopes=dict(type='list', elements='str'),
639                    service_account=dict(type='str'),
640                    metadata=dict(type='dict'),
641                    image_type=dict(type='str'),
642                    labels=dict(type='dict'),
643                    local_ssd_count=dict(type='int'),
644                    tags=dict(type='list', elements='str'),
645                    preemptible=dict(type='bool'),
646                    accelerators=dict(type='list', elements='dict', options=dict(accelerator_count=dict(type='int'), accelerator_type=dict(type='str'))),
647                    disk_type=dict(type='str'),
648                    min_cpu_platform=dict(type='str'),
649                    taints=dict(type='list', elements='dict', options=dict(key=dict(type='str'), value=dict(type='str'), effect=dict(type='str'))),
650                ),
651            ),
652            initial_node_count=dict(required=True, type='int'),
653            version=dict(type='str'),
654            autoscaling=dict(type='dict', options=dict(enabled=dict(type='bool'), min_node_count=dict(type='int'), max_node_count=dict(type='int'))),
655            management=dict(
656                type='dict', options=dict(auto_upgrade=dict(type='bool'), auto_repair=dict(type='bool'), upgrade_options=dict(type='dict', options=dict()))
657            ),
658            max_pods_constraint=dict(type='dict', options=dict(max_pods_per_node=dict(type='int'))),
659            conditions=dict(type='list', elements='dict', options=dict(code=dict(type='str'))),
660            cluster=dict(required=True, type='dict'),
661            location=dict(required=True, type='str', aliases=['region', 'zone']),
662        )
663    )
664
665    if not module.params['scopes']:
666        module.params['scopes'] = ['https://www.googleapis.com/auth/cloud-platform']
667
668    state = module.params['state']
669
670    fetch = fetch_resource(module, self_link(module))
671    changed = False
672
673    if fetch:
674        if state == 'present':
675            if is_different(module, fetch):
676                update(module, self_link(module))
677                fetch = fetch_resource(module, self_link(module))
678                changed = True
679        else:
680            delete(module, self_link(module))
681            fetch = {}
682            changed = True
683    else:
684        if state == 'present':
685            fetch = create(module, collection(module))
686            changed = True
687        else:
688            fetch = {}
689
690    fetch.update({'changed': changed})
691
692    module.exit_json(**fetch)
693
694
695def create(module, link):
696    auth = GcpSession(module, 'container')
697    return wait_for_operation(module, auth.post(link, resource_to_request(module)))
698
699
700def update(module, link):
701    auth = GcpSession(module, 'container')
702    return wait_for_operation(module, auth.put(link, resource_to_request(module)))
703
704
705def delete(module, link):
706    auth = GcpSession(module, 'container')
707    return wait_for_operation(module, auth.delete(link))
708
709
710def resource_to_request(module):
711    request = {
712        u'name': module.params.get('name'),
713        u'config': NodePoolConfig(module.params.get('config', {}), module).to_request(),
714        u'initialNodeCount': module.params.get('initial_node_count'),
715        u'version': module.params.get('version'),
716        u'autoscaling': NodePoolAutoscaling(module.params.get('autoscaling', {}), module).to_request(),
717        u'management': NodePoolManagement(module.params.get('management', {}), module).to_request(),
718        u'maxPodsConstraint': NodePoolMaxpodsconstraint(module.params.get('max_pods_constraint', {}), module).to_request(),
719        u'conditions': NodePoolConditionsArray(module.params.get('conditions', []), module).to_request(),
720    }
721    request = encode_request(request, module)
722    return_vals = {}
723    for k, v in request.items():
724        if v or v is False:
725            return_vals[k] = v
726
727    return return_vals
728
729
730def fetch_resource(module, link, allow_not_found=True):
731    auth = GcpSession(module, 'container')
732    return return_if_object(module, auth.get(link), allow_not_found)
733
734
735def self_link(module):
736    res = {
737        'project': module.params['project'],
738        'location': module.params['location'],
739        'cluster': replace_resource_dict(module.params['cluster'], 'name'),
740        'name': module.params['name'],
741    }
742    return "https://container.googleapis.com/v1/projects/{project}/locations/{location}/clusters/{cluster}/nodePools/{name}".format(**res)
743
744
745def collection(module):
746    res = {'project': module.params['project'], 'location': module.params['location'], 'cluster': replace_resource_dict(module.params['cluster'], 'name')}
747    return "https://container.googleapis.com/v1/projects/{project}/locations/{location}/clusters/{cluster}/nodePools".format(**res)
748
749
750def return_if_object(module, response, allow_not_found=False):
751    # If not found, return nothing.
752    if allow_not_found and response.status_code == 404:
753        return None
754
755    # If no content, return nothing.
756    if response.status_code == 204:
757        return None
758
759    try:
760        module.raise_for_status(response)
761        result = response.json()
762    except getattr(json.decoder, 'JSONDecodeError', ValueError):
763        module.fail_json(msg="Invalid JSON response with error: %s" % response.text)
764
765    if navigate_hash(result, ['error', 'errors']):
766        module.fail_json(msg=navigate_hash(result, ['error', 'errors']))
767
768    return result
769
770
771def is_different(module, response):
772    request = resource_to_request(module)
773    response = response_to_hash(module, response)
774
775    # Remove all output-only from response.
776    response_vals = {}
777    for k, v in response.items():
778        if k in request:
779            response_vals[k] = v
780
781    request_vals = {}
782    for k, v in request.items():
783        if k in response:
784            request_vals[k] = v
785
786    return GcpRequest(request_vals) != GcpRequest(response_vals)
787
788
789# Remove unnecessary properties from the response.
790# This is for doing comparisons with Ansible's current parameters.
791def response_to_hash(module, response):
792    return {
793        u'name': response.get(u'name'),
794        u'config': NodePoolConfig(response.get(u'config', {}), module).from_response(),
795        u'initialNodeCount': module.params.get('initial_node_count'),
796        u'status': response.get(u'status'),
797        u'statusMessage': response.get(u'statusMessage'),
798        u'version': module.params.get('version'),
799        u'autoscaling': NodePoolAutoscaling(response.get(u'autoscaling', {}), module).from_response(),
800        u'management': NodePoolManagement(response.get(u'management', {}), module).from_response(),
801        u'maxPodsConstraint': NodePoolMaxpodsconstraint(response.get(u'maxPodsConstraint', {}), module).from_response(),
802        u'conditions': NodePoolConditionsArray(response.get(u'conditions', []), module).from_response(),
803        u'podIpv4CidrSize': response.get(u'podIpv4CidrSize'),
804    }
805
806
807def async_op_url(module, extra_data=None):
808    if extra_data is None:
809        extra_data = {}
810    url = "https://container.googleapis.com/v1/projects/{project}/locations/{location}/operations/{op_id}"
811    combined = extra_data.copy()
812    combined.update(module.params)
813    return url.format(**combined)
814
815
816def wait_for_operation(module, response):
817    op_result = return_if_object(module, response)
818    if op_result is None:
819        return {}
820    status = navigate_hash(op_result, ['status'])
821    wait_done = wait_for_completion(status, op_result, module)
822    return fetch_resource(module, navigate_hash(wait_done, ['targetLink']))
823
824
825def wait_for_completion(status, op_result, module):
826    op_id = navigate_hash(op_result, ['name'])
827    op_uri = async_op_url(module, {'op_id': op_id})
828    while status != 'DONE':
829        raise_if_errors(op_result, ['error', 'errors'], module)
830        time.sleep(1.0)
831        op_result = fetch_resource(module, op_uri, False)
832        status = navigate_hash(op_result, ['status'])
833    return op_result
834
835
836def raise_if_errors(response, err_path, module):
837    errors = navigate_hash(response, err_path)
838    if errors is not None:
839        module.fail_json(msg=errors)
840
841
842# Google Container Engine API has its own layout for the create method,
843# defined like this:
844#
845# {
846#   'nodePool': {
847#     ... node pool data
848#   }
849# }
850#
851# Format the request to match the expected input by the API
852def encode_request(resource_request, module):
853    return {'nodePool': resource_request}
854
855
856class NodePoolConfig(object):
857    def __init__(self, request, module):
858        self.module = module
859        if request:
860            self.request = request
861        else:
862            self.request = {}
863
864    def to_request(self):
865        return remove_nones_from_dict(
866            {
867                u'machineType': self.request.get('machine_type'),
868                u'diskSizeGb': self.request.get('disk_size_gb'),
869                u'oauthScopes': self.request.get('oauth_scopes'),
870                u'serviceAccount': self.request.get('service_account'),
871                u'metadata': self.request.get('metadata'),
872                u'imageType': self.request.get('image_type'),
873                u'labels': self.request.get('labels'),
874                u'localSsdCount': self.request.get('local_ssd_count'),
875                u'tags': self.request.get('tags'),
876                u'preemptible': self.request.get('preemptible'),
877                u'accelerators': NodePoolAcceleratorsArray(self.request.get('accelerators', []), self.module).to_request(),
878                u'diskType': self.request.get('disk_type'),
879                u'minCpuPlatform': self.request.get('min_cpu_platform'),
880                u'taints': NodePoolTaintsArray(self.request.get('taints', []), self.module).to_request(),
881            }
882        )
883
884    def from_response(self):
885        return remove_nones_from_dict(
886            {
887                u'machineType': self.request.get(u'machineType'),
888                u'diskSizeGb': self.request.get(u'diskSizeGb'),
889                u'oauthScopes': self.request.get(u'oauthScopes'),
890                u'serviceAccount': self.request.get(u'serviceAccount'),
891                u'metadata': self.request.get(u'metadata'),
892                u'imageType': self.request.get(u'imageType'),
893                u'labels': self.request.get(u'labels'),
894                u'localSsdCount': self.request.get(u'localSsdCount'),
895                u'tags': self.request.get(u'tags'),
896                u'preemptible': self.request.get(u'preemptible'),
897                u'accelerators': NodePoolAcceleratorsArray(self.request.get(u'accelerators', []), self.module).from_response(),
898                u'diskType': self.request.get(u'diskType'),
899                u'minCpuPlatform': self.request.get(u'minCpuPlatform'),
900                u'taints': NodePoolTaintsArray(self.request.get(u'taints', []), self.module).from_response(),
901            }
902        )
903
904
905class NodePoolAcceleratorsArray(object):
906    def __init__(self, request, module):
907        self.module = module
908        if request:
909            self.request = request
910        else:
911            self.request = []
912
913    def to_request(self):
914        items = []
915        for item in self.request:
916            items.append(self._request_for_item(item))
917        return items
918
919    def from_response(self):
920        items = []
921        for item in self.request:
922            items.append(self._response_from_item(item))
923        return items
924
925    def _request_for_item(self, item):
926        return remove_nones_from_dict({u'acceleratorCount': item.get('accelerator_count'), u'acceleratorType': item.get('accelerator_type')})
927
928    def _response_from_item(self, item):
929        return remove_nones_from_dict({u'acceleratorCount': item.get(u'acceleratorCount'), u'acceleratorType': item.get(u'acceleratorType')})
930
931
932class NodePoolTaintsArray(object):
933    def __init__(self, request, module):
934        self.module = module
935        if request:
936            self.request = request
937        else:
938            self.request = []
939
940    def to_request(self):
941        items = []
942        for item in self.request:
943            items.append(self._request_for_item(item))
944        return items
945
946    def from_response(self):
947        items = []
948        for item in self.request:
949            items.append(self._response_from_item(item))
950        return items
951
952    def _request_for_item(self, item):
953        return remove_nones_from_dict({u'key': item.get('key'), u'value': item.get('value'), u'effect': item.get('effect')})
954
955    def _response_from_item(self, item):
956        return remove_nones_from_dict({u'key': item.get(u'key'), u'value': item.get(u'value'), u'effect': item.get(u'effect')})
957
958
959class NodePoolAutoscaling(object):
960    def __init__(self, request, module):
961        self.module = module
962        if request:
963            self.request = request
964        else:
965            self.request = {}
966
967    def to_request(self):
968        return remove_nones_from_dict(
969            {u'enabled': self.request.get('enabled'), u'minNodeCount': self.request.get('min_node_count'), u'maxNodeCount': self.request.get('max_node_count')}
970        )
971
972    def from_response(self):
973        return remove_nones_from_dict(
974            {u'enabled': self.request.get(u'enabled'), u'minNodeCount': self.request.get(u'minNodeCount'), u'maxNodeCount': self.request.get(u'maxNodeCount')}
975        )
976
977
978class NodePoolManagement(object):
979    def __init__(self, request, module):
980        self.module = module
981        if request:
982            self.request = request
983        else:
984            self.request = {}
985
986    def to_request(self):
987        return remove_nones_from_dict(
988            {
989                u'autoUpgrade': self.request.get('auto_upgrade'),
990                u'autoRepair': self.request.get('auto_repair'),
991                u'upgradeOptions': NodePoolUpgradeoptions(self.request.get('upgrade_options', {}), self.module).to_request(),
992            }
993        )
994
995    def from_response(self):
996        return remove_nones_from_dict(
997            {
998                u'autoUpgrade': self.request.get(u'autoUpgrade'),
999                u'autoRepair': self.request.get(u'autoRepair'),
1000                u'upgradeOptions': NodePoolUpgradeoptions(self.request.get(u'upgradeOptions', {}), self.module).from_response(),
1001            }
1002        )
1003
1004
1005class NodePoolUpgradeoptions(object):
1006    def __init__(self, request, module):
1007        self.module = module
1008        if request:
1009            self.request = request
1010        else:
1011            self.request = {}
1012
1013    def to_request(self):
1014        return remove_nones_from_dict({})
1015
1016    def from_response(self):
1017        return remove_nones_from_dict({})
1018
1019
1020class NodePoolMaxpodsconstraint(object):
1021    def __init__(self, request, module):
1022        self.module = module
1023        if request:
1024            self.request = request
1025        else:
1026            self.request = {}
1027
1028    def to_request(self):
1029        return remove_nones_from_dict({u'maxPodsPerNode': self.request.get('max_pods_per_node')})
1030
1031    def from_response(self):
1032        return remove_nones_from_dict({u'maxPodsPerNode': self.request.get(u'maxPodsPerNode')})
1033
1034
1035class NodePoolConditionsArray(object):
1036    def __init__(self, request, module):
1037        self.module = module
1038        if request:
1039            self.request = request
1040        else:
1041            self.request = []
1042
1043    def to_request(self):
1044        items = []
1045        for item in self.request:
1046            items.append(self._request_for_item(item))
1047        return items
1048
1049    def from_response(self):
1050        items = []
1051        for item in self.request:
1052            items.append(self._response_from_item(item))
1053        return items
1054
1055    def _request_for_item(self, item):
1056        return remove_nones_from_dict({u'code': item.get('code')})
1057
1058    def _response_from_item(self, item):
1059        return remove_nones_from_dict({u'code': item.get(u'code')})
1060
1061
1062if __name__ == '__main__':
1063    main()
1064