github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/kubernetes/resource_kubernetes_service.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/helper/schema"
     8  	pkgApi "k8s.io/kubernetes/pkg/api"
     9  	"k8s.io/kubernetes/pkg/api/errors"
    10  	api "k8s.io/kubernetes/pkg/api/v1"
    11  	kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
    12  )
    13  
    14  func resourceKubernetesService() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceKubernetesServiceCreate,
    17  		Read:   resourceKubernetesServiceRead,
    18  		Exists: resourceKubernetesServiceExists,
    19  		Update: resourceKubernetesServiceUpdate,
    20  		Delete: resourceKubernetesServiceDelete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"metadata": namespacedMetadataSchema("service", true),
    27  			"spec": {
    28  				Type:        schema.TypeList,
    29  				Description: "Spec defines the behavior of a service. http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
    30  				Required:    true,
    31  				MaxItems:    1,
    32  				Elem: &schema.Resource{
    33  					Schema: map[string]*schema.Schema{
    34  						"cluster_ip": {
    35  							Type:        schema.TypeString,
    36  							Description: "The IP address of the service. It is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. `None` can be specified for headless services when proxying is not required. Ignored if type is `ExternalName`. More info: http://kubernetes.io/docs/user-guide/services#virtual-ips-and-service-proxies",
    37  							Optional:    true,
    38  							ForceNew:    true,
    39  							Computed:    true,
    40  						},
    41  						"external_ips": {
    42  							Type:        schema.TypeSet,
    43  							Description: "A list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP.  A common example is external load-balancers that are not part of the Kubernetes system.",
    44  							Optional:    true,
    45  							Elem:        &schema.Schema{Type: schema.TypeString},
    46  							Set:         schema.HashString,
    47  						},
    48  						"external_name": {
    49  							Type:        schema.TypeString,
    50  							Description: "The external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid DNS name and requires `type` to be `ExternalName`.",
    51  							Optional:    true,
    52  						},
    53  						"load_balancer_ip": {
    54  							Type:        schema.TypeString,
    55  							Description: "Only applies to `type = LoadBalancer`. LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying this field when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.",
    56  							Optional:    true,
    57  						},
    58  						"load_balancer_source_ranges": {
    59  							Type:        schema.TypeSet,
    60  							Description: "If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature. More info: http://kubernetes.io/docs/user-guide/services-firewalls",
    61  							Optional:    true,
    62  							Elem:        &schema.Schema{Type: schema.TypeString},
    63  							Set:         schema.HashString,
    64  						},
    65  						"port": {
    66  							Type:        schema.TypeList,
    67  							Description: "The list of ports that are exposed by this service. More info: http://kubernetes.io/docs/user-guide/services#virtual-ips-and-service-proxies",
    68  							Required:    true,
    69  							MinItems:    1,
    70  							Elem: &schema.Resource{
    71  								Schema: map[string]*schema.Schema{
    72  									"name": {
    73  										Type:        schema.TypeString,
    74  										Description: "The name of this port within the service. All ports within the service must have unique names. Optional if only one ServicePort is defined on this service.",
    75  										Optional:    true,
    76  									},
    77  									"node_port": {
    78  										Type:        schema.TypeInt,
    79  										Description: "The port on each node on which this service is exposed when `type` is `NodePort` or `LoadBalancer`. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the `type` of this service requires one. More info: http://kubernetes.io/docs/user-guide/services#type--nodeport",
    80  										Computed:    true,
    81  										Optional:    true,
    82  									},
    83  									"port": {
    84  										Type:        schema.TypeInt,
    85  										Description: "The port that will be exposed by this service.",
    86  										Required:    true,
    87  									},
    88  									"protocol": {
    89  										Type:        schema.TypeString,
    90  										Description: "The IP protocol for this port. Supports `TCP` and `UDP`. Default is `TCP`.",
    91  										Optional:    true,
    92  										Default:     "TCP",
    93  									},
    94  									"target_port": {
    95  										Type:        schema.TypeInt,
    96  										Description: "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. This field is ignored for services with `cluster_ip = \"None\"`. More info: http://kubernetes.io/docs/user-guide/services#defining-a-service",
    97  										Required:    true,
    98  									},
    99  								},
   100  							},
   101  						},
   102  						"selector": {
   103  							Type:        schema.TypeMap,
   104  							Description: "Route service traffic to pods with label keys and values matching this selector. Only applies to types `ClusterIP`, `NodePort`, and `LoadBalancer`. More info: http://kubernetes.io/docs/user-guide/services#overview",
   105  							Optional:    true,
   106  						},
   107  						"session_affinity": {
   108  							Type:        schema.TypeString,
   109  							Description: "Used to maintain session affinity. Supports `ClientIP` and `None`. Defaults to `None`. More info: http://kubernetes.io/docs/user-guide/services#virtual-ips-and-service-proxies",
   110  							Optional:    true,
   111  							Default:     "None",
   112  						},
   113  						"type": {
   114  							Type:        schema.TypeString,
   115  							Description: "Determines how the service is exposed. Defaults to `ClusterIP`. Valid options are `ExternalName`, `ClusterIP`, `NodePort`, and `LoadBalancer`. `ExternalName` maps to the specified `external_name`. More info: http://kubernetes.io/docs/user-guide/services#overview",
   116  							Optional:    true,
   117  							Default:     "ClusterIP",
   118  						},
   119  					},
   120  				},
   121  			},
   122  		},
   123  	}
   124  }
   125  
   126  func resourceKubernetesServiceCreate(d *schema.ResourceData, meta interface{}) error {
   127  	conn := meta.(*kubernetes.Clientset)
   128  
   129  	metadata := expandMetadata(d.Get("metadata").([]interface{}))
   130  	svc := api.Service{
   131  		ObjectMeta: metadata,
   132  		Spec:       expandServiceSpec(d.Get("spec").([]interface{})),
   133  	}
   134  	log.Printf("[INFO] Creating new service: %#v", svc)
   135  	out, err := conn.CoreV1().Services(metadata.Namespace).Create(&svc)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	log.Printf("[INFO] Submitted new service: %#v", out)
   140  	d.SetId(buildId(out.ObjectMeta))
   141  
   142  	return resourceKubernetesServiceRead(d, meta)
   143  }
   144  
   145  func resourceKubernetesServiceRead(d *schema.ResourceData, meta interface{}) error {
   146  	conn := meta.(*kubernetes.Clientset)
   147  
   148  	namespace, name := idParts(d.Id())
   149  	log.Printf("[INFO] Reading service %s", name)
   150  	svc, err := conn.CoreV1().Services(namespace).Get(name)
   151  	if err != nil {
   152  		log.Printf("[DEBUG] Received error: %#v", err)
   153  		return err
   154  	}
   155  	log.Printf("[INFO] Received service: %#v", svc)
   156  	err = d.Set("metadata", flattenMetadata(svc.ObjectMeta))
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	flattened := flattenServiceSpec(svc.Spec)
   162  	log.Printf("[DEBUG] Flattened service spec: %#v", flattened)
   163  	err = d.Set("spec", flattened)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func resourceKubernetesServiceUpdate(d *schema.ResourceData, meta interface{}) error {
   172  	conn := meta.(*kubernetes.Clientset)
   173  
   174  	namespace, name := idParts(d.Id())
   175  
   176  	ops := patchMetadata("metadata.0.", "/metadata/", d)
   177  	if d.HasChange("spec") {
   178  		diffOps := patchServiceSpec("spec.0.", "/spec/", d)
   179  		ops = append(ops, diffOps...)
   180  	}
   181  	data, err := ops.MarshalJSON()
   182  	if err != nil {
   183  		return fmt.Errorf("Failed to marshal update operations: %s", err)
   184  	}
   185  	log.Printf("[INFO] Updating service %q: %v", name, string(data))
   186  	out, err := conn.CoreV1().Services(namespace).Patch(name, pkgApi.JSONPatchType, data)
   187  	if err != nil {
   188  		return fmt.Errorf("Failed to update service: %s", err)
   189  	}
   190  	log.Printf("[INFO] Submitted updated service: %#v", out)
   191  	d.SetId(buildId(out.ObjectMeta))
   192  
   193  	return resourceKubernetesServiceRead(d, meta)
   194  }
   195  
   196  func resourceKubernetesServiceDelete(d *schema.ResourceData, meta interface{}) error {
   197  	conn := meta.(*kubernetes.Clientset)
   198  
   199  	namespace, name := idParts(d.Id())
   200  	log.Printf("[INFO] Deleting service: %#v", name)
   201  	err := conn.CoreV1().Services(namespace).Delete(name, &api.DeleteOptions{})
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	log.Printf("[INFO] Service %s deleted", name)
   207  
   208  	d.SetId("")
   209  	return nil
   210  }
   211  
   212  func resourceKubernetesServiceExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   213  	conn := meta.(*kubernetes.Clientset)
   214  
   215  	namespace, name := idParts(d.Id())
   216  	log.Printf("[INFO] Checking service %s", name)
   217  	_, err := conn.CoreV1().Services(namespace).Get(name)
   218  	if err != nil {
   219  		if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
   220  			return false, nil
   221  		}
   222  		log.Printf("[DEBUG] Received error: %#v", err)
   223  	}
   224  	return true, err
   225  }