defmodule GCloudex.ComputeEngine.Impl do @moduledoc """ """ defmacro __using__(:compute_engine) do quote do use GCloudex.ComputeEngine.Request @project_id GCloudex.get_project_id @instance_ep "https://www.googleapis.com/compute/v1/projects/#{@project_id}/zones" @no_zone_ep "https://www.googleapis.com/compute/v1/projects/#{@project_id}" ################### ### Autoscalers ### ################### @doc """ Retrieves a list of autoscalers contained within the specified 'zone' and according to the 'query_params' if provided. """ @spec list_autoscalers(zone :: binary, query_params :: map) :: HTTPResponse.t def list_autoscalers(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/zones/#{zone}/autoscalers", [], "", query end @doc """ Returns the specified 'autoscaler' resource if it exists in the given 'zone'. """ @spec get_autoscaler(zone :: binary, autoscaler :: binary, fields :: binary) :: HTTPResponse.t def get_autoscaler(zone, autoscaler, fields \\ "") do query = fields_binary_to_map fields request :get, @no_zone_ep <> "/zones/#{zone}/autoscalers/#{autoscaler}", [], "", query end @doc """ Creates an autoscaler in the given 'zone' using the data provided in 'autoscaler_resource'. For the properties and structure of the 'autoscaler_resource' check https://cloud.google.com/compute/docs/reference/latest/autoscalers#resource """ @spec insert_autoscaler(zone :: binary, autoscaler_resource :: Map.t, fields :: binary) :: HTTPResponse.t | no_return def insert_autoscaler(zone, autoscaler_resource, fields \\ "") do query = fields_binary_to_map fields body = autoscaler_resource |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/autoscalers", [{"Content-Type", "application/json"}], body, query) end @doc """ Updates 'autoscaler_name' in the specified 'zone' using the data included in the 'autoscaler_resource'. This function supports patch semantics. """ @spec patch_autoscaler(zone :: binary, autoscaler_name :: binary, autoscaler_resource :: Map.t, fields :: binary) :: HTTPResponse.t def patch_autoscaler(zone, autoscaler_name, autoscaler_resource, fields \\ "") do query = if fields == "" do %{"autoscaler" => autoscaler_name} |> URI.encode_query else %{"autoscaler" => autoscaler_name, "fields" => fields} |> URI.encode_query end body = autoscaler_resource |> Poison.encode! request( :patch, @no_zone_ep <> "/zones/#{zone}/autoscalers", [{"Content-Type", "application/json"}], body, query) end @doc """ Updates an autoscaler in the specified 'zone' using the data included in the 'autoscaler_resource'. The 'autoscaler_name' may be provided but it's optional. """ @spec update_autoscaler(zone :: binary, autoscaler_name :: binary, autoscaler_resource :: Map.t, fields :: binary) :: HTTPResponse.t def update_autoscaler(zone, autoscaler_name \\ "", autoscaler_resource, fields \\ "") do body = autoscaler_resource |> Poison.encode! query = case {autoscaler_name == "", fields == ""} do {true, true} -> "" {true, false} -> %{"fields" => fields} |> URI.encode_query {false, true} -> %{"autoscaler" => autoscaler_name} |> URI.encode_query {false, false} -> %{"autoscaler" => autoscaler_name, "fields" => fields} |> URI.encode_query end request( :put, @no_zone_ep <> "/zones/#{zone}/autoscalers", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified 'autoscaler' if it exists in the given 'zone'. """ @spec delete_autoscaler(zone :: binary, autoscaler :: binary, fields :: binary) :: HTTPResponse.t def delete_autoscaler(zone, autoscaler, fields \\ "") do query = fields_binary_to_map fields request(:delete, @no_zone_ep <> "/zones/#{zone}/autoscalers/#{autoscaler}", [], "", query) end @doc """ Retrieves an aggregated list of autoscalers according to the given 'query_params' if provided. """ @spec aggregated_list_of_autoscalers(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_autoscalers(query_params \\ %{}) do query = query_params |> URI.encode_query request(:get, @no_zone_ep <> "/aggregated/autoscalers", [], "", query) end ################# ### DiskTypes ### ################# @doc """ Retrieves a list of disk types available to the specified 'zone' according to the given 'query_params' if provided. """ @spec list_disk_types(zone :: binary, query_params :: Map.t) :: HTTPResponse.t def list_disk_types(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/zones/#{zone}/diskTypes", [], "", query end @doc """ Returns the specified 'disk_type' if it exists in the given 'zone'. """ @spec get_disk_type(zone :: binary, disk_type :: binary, fields :: binary) :: HTTPResponse.t def get_disk_type(zone, disk_type, fields \\ "") do query = fields_binary_to_map fields request( :get, @no_zone_ep <> "/zones/#{zone}/diskTypes/#{disk_type}", [], "", query) end @doc """ Retrieves an aggregated list of Disk Types according to the 'query_params' if provided. """ @spec aggregated_list_of_disk_types(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_disk_types(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/aggregated/diskTypes", [], "", query end ############# ### Disks ### ############# @doc """ Retrieves a list of persistent disks contained within the specified 'zone' and according to the given 'query_params' if provided. """ @spec list_disks(zone :: binary, query_params :: Map.t) :: HTTPResponse.t def list_disks(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/zones/#{zone}/disks", [], "", query end @doc """ Returns a specified persistent 'disk' if it exists in the given 'zone'. The HTTP reply contains a Disk Resource in the body. """ @spec get_disk(zone :: binary, disk :: binary, fields :: binary) :: HTTPResponse.t def get_disk(zone, disk, fields \\ "") do query = fields_binary_to_map fields request :get, @no_zone_ep <> "/zones/#{zone}/disks/#{disk}", [], "", query end @doc """ Creates a persistent disk in the specified 'zone' using the data in the 'disk_resource'. You can create a disk with a 'source_image' or a sourceSnapshot (provided in the 'disk_resource'), or create an empty 500 GB data disk by omitting all properties. You can also create a disk that is larger than the default size by specifying the sizeGb property in the 'disk_resource'. """ @spec insert_disk(zone :: binary, disk_resource :: map, source_image :: binary, fields :: binary) :: HTTPResponse.t def insert_disk(zone, disk_resource, source_image \\ "", fields \\ "") do if not Map.has_key?(disk_resource, "name") do raise ArgumentError, message: "The Disk Resource must contain at least the 'name' key." end query = case {source_image != "", fields != ""} do {true, true} -> fields_binary_to_map(fields) <> "&sourceImage=#{source_image}" {true, false} -> %{"sourceImage" => source_image} |> URI.encode_query {false, true} -> fields_binary_to_map fields {false, false} -> %{} |> URI.encode_query end body = disk_resource |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/disks", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified persistent 'disk' if it exists in the given 'zone'. """ @spec delete_disk(zone :: binary, disk :: binary, fields :: binary) :: HTTPResponse.t def delete_disk(zone, disk, fields \\ "") do query = fields_binary_to_map fields request :delete, @no_zone_ep <> "/zones/#{zone}/disks/#{disk}", [], "", query end @doc """ Resizes the specified persistent 'disk' if it exists in the given 'zone' to the provided 'size_gb'. """ @spec resize_disk(zone :: binary, disk :: binary, size_gb :: pos_integer, fields :: binary) :: HTTPResponse.t def resize_disk(zone, disk, size_gb, fields \\ "") when size_gb > 0 do query = fields_binary_to_map fields body = %{"sizeGb" => size_gb} |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/disks/#{disk}/resize", [{"Content-Type", "application/json"}], body, query) end @doc """ Retrieves an aggregated list of persistent disks according to the given 'query_params' if they're provided. """ @spec aggregated_list_of_disks(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_disks(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/aggregated/disks", [], "", query end @doc """ Creates a snapshot of a specified persistent 'disk' if it exists in the given 'zone' and according to the given 'resource'. The 'request' map must contain the keys "name" and "description" and "name" must obey the refex '(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)'. """ @spec create_snapshot(zone :: binary, disk :: binary, request :: Map.t, fields :: binary) :: HTTPResponse.t def create_snapshot(zone, disk, request, fields \\ "") when request != %{} do query = fields_binary_to_map fields body = request |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/disks/#{disk}/createSnapshot", [{"Content-Type", "application/json"}], body, query) end ################# ### Firewalls ### ################# @doc """ Retrieves the list of firewall rules according to the 'query_params' if provided. """ @spec list_firewalls(query_params :: Map.t) :: HTTPResponse.t def list_firewalls(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/global/firewalls", [], "", query end @doc """ Returns the specified 'firewall'. """ @spec get_firewall(firewall :: binary, fields :: binary) :: HTTPResponse.t def get_firewall(firewall, fields \\ "") do query = fields_binary_to_map fields request :get, @no_zone_ep <> "/global/firewalls/#{firewall}", [], "", query end @doc """ Creates a new firewall using the data included in 'firewall_resource'. For information about the structure and properties of Firewall Resources check https://cloud.google.com/compute/docs/reference/latest/firewalls#resource """ @spec insert_firewall(firewall_resource :: Map.t, fields :: binary) :: HTTPResponse.t def insert_firewall(firewall_resource, fields \\ "") when is_map(firewall_resource) do query = fields_binary_to_map fields body = firewall_resource |> Poison.encode! request( :post, @no_zone_ep <> "/global/firewalls", [{"Content-Type", "application/json"}], body, query) end @doc """ Updates the specified 'firewall' rule with the data included in the 'firewall_resource'. This function supports patch semantics. """ @spec patch_firewall(firewall :: binary, firewall_resource :: Map.t, fields :: binary) :: HTTPResponse.t def patch_firewall(firewall, firewall_resource, fields \\ "") when is_map(firewall_resource) do query = fields_binary_to_map fields body = firewall_resource |> Poison.encode! request( :patch, @no_zone_ep <> "/global/firewalls/#{firewall}", [{"Content-Type", "application/json"}], body, query) end @doc """ Updates the specified 'firewall' rule with the data included in the 'firewall_resource'. """ @spec update_firewall(firewall :: binary, firewall_resource :: map, fields :: binary) :: HTTPResponse.t def update_firewall(firewall, firewall_resource, fields \\ "") when is_map(firewall_resource) do query = fields_binary_to_map fields body = firewall_resource |> Poison.encode! request( :put, @no_zone_ep <> "/global/firewalls/#{firewall}", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified 'firewall'. """ @spec delete_firewall(firewall :: binary, fields :: binary) :: HTTPResponse.t def delete_firewall(firewall, fields \\ "") do query = fields_binary_to_map fields request( :delete, @no_zone_ep <> "/global/firewalls/#{firewall}", [], "", query) end ############## ### Images ### ############## @doc """ Retrieves the list of private images available (project scoped only). """ @spec list_images(query_params :: Map.t) :: HTTPResponse.t def list_images(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/global/images", [], "", query end @doc """ Returns the specified private 'image'. For public images use 'get_public_image/3'. """ @spec get_image(image :: binary, fields :: binary) :: HTTPResponse.t def get_image(image, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request(:get, @no_zone_ep <> "/global/images/#{image}", [], "", query) end @doc""" Returns the specified public 'image'. For private images use 'get_image/2'. """ @spec get_public_image(image :: binary, project :: binary, fields :: binary) :: HTTPResponse.t def get_public_image(image, project, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end new_ep = @no_zone_ep |> String.replace(@project_id, project) request(:get, new_ep <> "/global/images/#{image}", [], "", query) end @doc """ Creates an image with the provided 'image_resource'. """ @spec insert_image_with_resource(image_resource :: Map.t, fields :: binary) :: HTTPResponse.t def insert_image_with_resource(image_resource, fields \\ "") when is_map(image_resource) do body = image_resource |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @no_zone_ep <> "/global/images", [{"Content-Type", "application/json"}], body, query) end @doc """ Creates an image with the given 'name' and 'source_url'. This function uses the minimal amount of parameters needed to build the image. For a more detailed image creation use insert_image_with_resource/2 where more complex Image Resources can be passed. """ @spec insert_image(name :: binary, source_url :: binary, fields :: binary) :: HTTPResponse.t def insert_image(name, source_url, fields \\ "") do body = %{"name" => name, "rawDisk" => %{"source" => source_url}} |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @no_zone_ep <> "/global/images", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified 'image'. """ @spec delete_image(image :: binary, fields :: binary) :: HTTPResponse.t def delete_image(image, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request(:delete, @no_zone_ep <> "/global/images/#{image}", [], "", query) end @doc """ Sets the deprecation status of an 'image' using the data provided in the 'request_params'. """ @spec deprecate_image(image :: binary, request_params :: Map.t, fields :: binary) :: HTTPResponse.t def deprecate_image(image, request_params, fields \\ "") do body = request_params |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @no_zone_ep <> "/global/images/#{image}/deprecate", [{"Content-Type", "application/json"}], body, query) end ###################### ### InstanceGroups ### ###################### @doc """ Retrieves the list of instance groups that are located in the specified 'zone'. """ @spec list_instance_groups(zone :: binary, query_params :: Map.t) :: HTTPResponse.t def list_instance_groups(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/zones/#{zone}/instanceGroups", [], "", query end @doc """ Lists the instances in the specified 'instance_group' if it exists in the given 'zone'. A filter for the state of the instances can be passed through 'instance_state'. """ @spec list_instances_in_group(zone :: binary, instance_group :: binary, instance_state :: binary, query_params :: Map.t) :: HTTPResponse.t def list_instances_in_group(zone, instance_group, instance_state \\ "", query_params \\ %{}) do query = query_params |> URI.encode_query body = %{"instanceState" => instance_state} |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}/listInstances", [{"Content-Type", "application/json"}], body, query) end @doc """ Returns the specified 'instance_group' if it exists in the given 'zone'. """ @spec get_instance_group(zone :: binary, instance_group :: binary, fields :: binary) :: HTTPResponse.t def get_instance_group(zone, instance_group, fields \\ "") do query = fields_binary_to_map fields request(:get, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}", [], "", query) end @doc """ Creates an Instance Group in the specified 'zone' using the data provided in 'instance_group_resource'. For the structure and properties required in the Instance Group Resources check https://cloud.google.com/compute/docs/reference/latest/instanceGroups#resource """ @spec insert_instance_group(zone :: binary, instance_group_resource :: map, fields :: binary) :: HTTPResponse.t def insert_instance_group(zone, instance_group_resource, fields \\ "") when is_map(instance_group_resource) do query = fields_binary_to_map fields body = instance_group_resource |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/instanceGroups", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified 'instance_group' if it exists in the given 'zone'. """ @spec delete_instance_group(zone :: binary, instance_group :: binary, fields :: binary) :: HTTPResponse.t def delete_instance_group(zone, instance_group, fields \\ "") do query = fields_binary_to_map fields request( :delete, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}", [], "", query) end @doc """ Retrieves the list of Instance Groups and sorts them by zone. """ @spec aggregated_list_of_instance_groups(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_instance_groups(query_params \\ %{}) do query = query_params |> URI.encode_query request(:get, @no_zone_ep <> "/aggregated/instanceGroups", [], "", query) end @doc """ Adds a list of 'instances' to the specified 'instance_group' if it exists in the given 'zone'. """ @spec add_instances_to_group(zone :: binary, instance_group :: binary, instances :: [binary], fields :: binary) :: HTTPResponse.t def add_instances_to_group(zone, instance_group, instances, fields \\ "") do query = fields_binary_to_map fields body = %{"instances" => build_list_of_instances(instances, [])} |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}/addInstances", [{"Content-Type", "application/json"}], body, query) end @doc """ Removes the 'instances' from the provided 'instance_group' if it exists in the given 'zone'. """ @spec remove_instances_from_group(zone :: binary, insance_group :: binary, instances :: [binary], fields :: binary) :: HTTPResponse.t def remove_instances_from_group(zone, instance_group, instances, fields \\ "") do query = fields_binary_to_map fields body = %{"instances" => build_list_of_instances(instances, [])} |> Poison.encode! request( :post, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}/removeInstances", [{"Content-Type", "application/json"}], body, query) end @doc """ Sets the named 'ports' for the specified 'instance_group' if it exists in the given 'zone'. The 'ports' must be passed as a list of tuples with the format {, }. """ @spec set_named_ports_for_group(zone :: binary, instance_group :: binary, ports :: [{name :: binary, port :: binary}], fingerprint :: binary, fields :: binary) :: HTTPResponse.t def set_named_ports_for_group(zone, instance_group, ports, fingerprint \\ "", fields \\ "") do query = fields_binary_to_map fields body = %{"namedPorts" => build_list_of_ports(ports, [])} body = if fingerprint != "" do body |> Map.put_new("fingerprint", fingerprint) |> Poison.encode! else body |> Poison.encode! end request( :post, @no_zone_ep <> "/zones/#{zone}/instanceGroups/#{instance_group}/setNamedPorts", [{"Content-Type", "application/json"}], body, query) end defp build_list_of_instances([head | []], state), do: state ++ [%{"instance" => head}] defp build_list_of_instances([head | tail], state) do build_list_of_instances tail, state ++ [%{"instance" => head}] end defp build_list_of_ports([_head = {name, port} | []], state), do: state ++ [%{"name" => name, "port" => port}] defp build_list_of_ports([_head = {name, port} | tail], state) do build_list_of_ports tail, state ++ [%{"name" => name, "port" => port}] end ################# ### Instances ### ################# @doc """ Lists all instances in the given 'zone' obeying the given 'query_params' if present. """ @spec list_instances(zone :: binary, query_params :: Map.t) :: HTTPResponse.t def list_instances(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @instance_ep <> "/#{zone}/instances", [], "", query end @doc """ Returns the data about the 'instance' in the given 'zone' if it exists. """ @spec get_instance(zone :: binary, instance :: binary, fields :: binary) :: HTTPResponse.t def get_instance(zone, instance, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :get, @instance_ep <> "/#{zone}/instances/#{instance}", [], "", query) end @doc """ Creates a new Virtual Machine instance on Google Compute Engine in the given 'zone' and with properties defined in 'instance_resource'. Example of the minimal Instance Resource the API will accept: %{ "disks" => [ %{ "autoDelete" => true, "boot" => true, "initializeParams" => %{ "sourceImage" => "projects/debian-cloud/global/images/debian-8-jessie-v20160119" }, "type" => "PERSISTENT" } ], "machineType" => "zones/europe-west1-d/machineTypes/f1-micro", "name" => "example-instance", "networkInterfaces" => [ %{ "accessConfigs" => [ %{ "name" => "External NAT", "type" => "ONE_TO_ONE_NAT" } ], "network" => "global/networks/default" } ] } For the 'instance_resource' check the documentation at: https://cloud.google.com/compute/docs/reference/latest/instances#resource """ @spec insert_instance(zone :: binary, instance_resource :: Map.t, fields :: binary) :: HTTPResponse.t def insert_instance(zone, instance_resource, fields \\ "") do body = instance_resource |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the given 'instance' if it exists in the given 'zone'. """ @spec delete_instance(zone :: binary, instance :: binary, fields :: binary) :: HTTPResponse.t def delete_instance(zone, instance, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :delete, @instance_ep <> "/#{zone}/instances/#{instance}", [], "", query) end @doc """ Starts a stopped 'instance' if it exists in the given 'zone'. """ @spec start_instance(zone :: binary, instance :: binary, fields :: binary) :: HTTPResponse.t def start_instance(zone, instance, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/start", [{"Content-Type", "application/json"}], "", query) end @doc """ Stops a running 'instance' if it exists in the given 'zone'. """ @spec stop_instance(zone :: binary, instance :: binary, fields :: binary) :: HTTPResponse.t def stop_instance(zone, instance, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/stop", [{"Content-Type", "application/json"}], "", query) end @doc """ Performs a hard reset on the 'instance' if it exists in the given 'zone'. """ @spec reset_instance(zone :: binary, instance :: binary, fields :: binary) :: HTTPResponse.t def reset_instance(zone, instance, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/reset", [{"Content-Type", "application/json"}], "", query) end @doc """ Adds an access config to the 'instance' if it exists in the given 'zone'. The kind and type will automatically be added with the default (and only possible) values. """ @spec add_access_config(zone :: binary, instance :: binary, network_interface :: binary, name :: binary, nat_ip :: binary, fields :: binary) :: HTTPResponse.t def add_access_config(zone, instance, network_interface, name, nat_ip \\ "", fields \\ "") do body = %{ "kind" => "compute#accessConfig", "type" => "ONE_TO_ONE_NAT", "name" => name } body = if nat_ip != "" do body |> Map.put_new("natIP", nat_ip) else body end query = if fields == "" do %{"networkInterface" => network_interface} |> URI.encode_query else %{"networkInterface" => network_interface, "fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/addAccessConfig", [{"Content-Type", "application/json"}], body |> Poison.encode!, query) end @doc """ Deletes te 'access_config' from the 'instance' 'network_interface' if the 'instance' exists in the given 'zone'. """ @spec delete_access_config(zone :: binary, instance :: binary, access_config :: binary, network_interface :: binary, fields :: binary) :: HTTPResponse.t def delete_access_config(zone, instance, access_config, network_interface, fields \\ "") do query = if fields == "" do %{"accessConfig" => access_config, "networkInterface" => network_interface} |> URI.encode_query else %{ "accessConfig" => access_config, "networkInterface" => network_interface, "fields" => fields } |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/deleteAccessConfig", [{"Content-Type", "application/json"}], "", query) end @doc """ Retrieves aggregated list of instances according to the specified 'query_params' if present. """ @spec aggregated_list_of_instances(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_instances(query_params \\ %{}) do query = query_params |> URI.encode_query request( :get, @no_zone_ep <> "/aggregated/instances", [], "", query) end @doc """ Attaches the 'disk_resource' to the 'instance' if it exists in the given 'zone'. Disk Resource structure: { "kind": "compute#attachedDisk", "index": integer, "type": string, "mode": string, "source": string, "deviceName": string, "boot": boolean, "initializeParams": { "diskName": string, "sourceImage": string, "diskSizeGb": long, "diskType": string }, "autoDelete": boolean, "licenses": [ string ], "interface": string } """ @spec attach_disk(zone :: binary, instance :: binary, disk_resource :: map, fields :: binary) :: HTTPResponse.t def attach_disk(zone, instance, disk_resource, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/attachDisk", [{"Content-Type", "application/json"}], disk_resource |> Poison.encode!, query) end @doc """ Detaches the disk with 'device_name' from the 'instance' if it exists in the given 'zone'. """ @spec detach_disk(zone :: binary, instance :: binary, device_name :: binary, fields :: binary) :: HTTPResponse.t def detach_disk(zone, instance, device_name, fields \\ "") do query = if fields == "" do %{"deviceName" => device_name} |> URI.encode_query else %{"deviceName" => device_name, "fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/detachDisk", [{"Content-Type", "application/json"}], "", query) end @doc """ Sets the 'auto_delete' flag for the disk with 'device_name' attached to 'instance' if it exists in the given 'zone'. """ @spec set_disk_auto_delete(zone :: binary, instance :: binary, auto_delete :: boolean, device_name :: binary, fields :: binary) :: HTTPResponse.t def set_disk_auto_delete(zone, instance, auto_delete, device_name, fields \\ "") do query = if fields == "" do %{"deviceName" => device_name, "autoDelete" => auto_delete} |> URI.encode_query else %{ "deviceName" => device_name, "autoDelete" => auto_delete, "fields" => fields } |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/setDiskAutoDelete", [{"Content-Type", "application/json"}], "", query) end @doc """ Returns the specified 'instance' serial 'port' output if the 'instance' exists in the given 'zone'. The 'port' accepted values are from 1 to 4, inclusive. """ @spec get_serial_port_output(zone :: binary, instance :: binary, port :: 1..4, fields :: binary) :: HTTPResponse.t def get_serial_port_output(zone, instance, port \\ 1, fields \\ "") do port = if port == 1 do port else port end query = if fields == "" do %{"port" => port} |> URI.encode_query else %{"port" => port, "fields" => fields} |> URI.encode_query end request( :get, @instance_ep <> "/#{zone}/instances/#{instance}/serialPort", [], "", query) end @doc """ Changes the Machine Type for a stopped 'instance' to the specified 'machine_type' if the 'instance' exists in the given 'zone'. """ @spec set_machine_type(zone :: binary, instance :: binary, machine_type :: binary, fields :: binary) :: HTTPResponse.t def set_machine_type(zone, instance, machine_type, fields \\ "") do body = %{"machineType" => machine_type} |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/setMachineType", [{"Content-Type", "application/json"}], body, query) end @doc """ Sets metadata for the specified 'instance' to the data specified in 'fingerprint' and 'items'. The 'instance' must exist in the given 'zone'. The 'items' should be passed as a list of maps: [ %{ "key" => "some key value", "value" => "some value" }, %{ "key" => "some other key value", "value" => "some other value" } ] """ @spec set_metadata(zone :: binary, instance :: binary, fingerprint :: binary, items :: list(map), fields :: binary) :: HTTPResponse.t def set_metadata(zone, instance, fingerprint, items, fields \\ "") do body = %{"kind" => "compute#metadata"} |> Map.put_new("fingerprint", fingerprint) |> Map.put_new("items", items) |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/setMetadata", [{"Content-Type", "application/json"}], body, query) end @doc """ Sets an 'instance' scheduling options if it exists in the given 'zone'. The 'preemptible' option cannot be changed after instance creation. """ @spec set_scheduling(zone :: binary, instance :: binary, {on_host_maintenance :: binary, automatic_restart :: boolean, preemptible :: boolean}, fields :: binary) :: HTTPResponse.t def set_scheduling(zone, instance, {on_host_maintenance, automatic_restart, preemptible}, fields \\ "") do body = %{ "onHostMaintenance" => on_host_maintenance, "automaticRestart" => automatic_restart, "preemptible" => preemptible } |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/setScheduling", [{"Content-Type", "application/json"}], body, query) end @doc """ Sets tags for the specified 'instance' to the given 'fingerprint' and 'items'. The 'instance' must exist in the given 'zone'. """ @spec set_tags(zone :: binary, instance :: binary, fingerprint :: binary, items :: list(binary), fields :: binary) :: HTTPResponse.t def set_tags(zone, instance, fingerprint, items, fields \\ "") do body = %{"fingerprint" => fingerprint, "items" => items} |> Poison.encode! query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :post, @instance_ep <> "/#{zone}/instances/#{instance}/setTags", [{"Content-Type", "application/json"}], body, query) end ################ ### Licenses ### ################ @doc """ Returns the specified 'license' resource. """ @spec get_license(binary, binary) :: HTTPResponse.t def get_license(license, fields \\ "") do query = fields_binary_to_map fields request :get, @no_zone_ep <> "/global/licenses/#{license}", [], "", query end ##################### ### Machine Types ### ##################### @doc """ Retrieves a list of machine types available in the specified 'zone' and that fit in the given 'query_params' if present. """ @spec list_machine_types(zone :: binary, query_params :: Map.t) :: HTTPResponse.t def list_machine_types(zone, query_params \\ %{}) do query = query_params |> URI.encode_query request(:get, @instance_ep <> "/#{zone}/machineTypes", [], "", query) end @doc """ Returns the specified 'machine_type' in the given 'zone'. """ @spec get_machine_type(zone :: binary, machine_type :: binary, fields :: binary) :: HTTPResponse.t def get_machine_type(zone, machine_type, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request( :get, @instance_ep <> "/#{zone}/machineTypes/#{machine_type}", [], "", query) end @doc """ Returns an aggragated list of machine types following the specified 'query_params' if present. """ @spec aggregated_list_of_machine_types(query_params :: Map.t) :: HTTPResponse.t def aggregated_list_of_machine_types(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/aggregated/machineTypes", [], "", query end ################ ### Networks ### ################ @doc """ Retrieves the list of networks available according to the given 'query_params' if provided. """ @spec list_networks(query_params :: Map.t) :: HTTPResponse.t def list_networks(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/global/networks", [], "", query end @doc """ Returns the speciefied 'network'. """ @spec get_network(network :: binary, fields :: binary) :: HTTPResponse.t def get_network(network, fields \\ "") do query = fields_binary_to_map fields request :get, @no_zone_ep <> "/global/networks/#{network}", [], "", query end @doc """ Creates a network using the data specified in 'network_resource'. To read about the structure and properties of the Network Resource check https://cloud.google.com/compute/docs/reference/latest/networks#resource. """ @spec insert_network(network_resource :: Map.t, fields :: binary) :: HTTPResponse.t | no_return def insert_network(network_resource, fields \\ "") when is_map(network_resource) do query = fields_binary_to_map fields body = network_resource |> Poison.encode! request( :post, @no_zone_ep <> "/global/networks", [{"Content-Type", "application/json"}], body, query) end @doc """ Deletes the specified 'network'. """ @spec delete_network(network :: binary, fields :: binary) :: HTTPResponse.t def delete_network(network, fields \\ "") do query = fields_binary_to_map fields request( :delete, @no_zone_ep <> "/global/networks/#{network}", [], "", query) end ############### ### Regions ### ############### @doc """ Retrieves a list of region resources according to the given 'query_params'. """ @spec list_regions(query_params :: Map.t) :: HTTPResponse.t def list_regions(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/regions", [], "", query end @doc """ Returns the specified 'region' resource. """ @spec get_region(region :: binary, fields :: binary) :: HTTPResponse.t def get_region(region, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request :get, @no_zone_ep <> "/regions/#{region}", [], "", query end ############# ### Zones ### ############# @doc """ Retrieves the list of Zone resources available according to the given 'query_params'. """ @spec list_zones(query_params :: Map.t) :: HTTPResponse.t def list_zones(query_params \\ %{}) do query = query_params |> URI.encode_query request :get, @no_zone_ep <> "/zones", [], "", query end @doc """ Returns the specified 'zone' resource. """ @spec get_zone(zone :: binary, fields :: binary) :: HTTPResponse.t def get_zone(zone, fields \\ "") do query = if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end request :get, @no_zone_ep <> "/zones/#{zone}", [], "", query end ############### ### Helpers ### ############### defp fields_binary_to_map(fields) do if fields == "" do fields else %{"fields" => fields} |> URI.encode_query end end end end end