diff --git a/backend/local/counthookaction_string.go b/backend/local/counthookaction_string.go index 92b2624a5..574a8c6cb 100644 --- a/backend/local/counthookaction_string.go +++ b/backend/local/counthookaction_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT. +// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT package local diff --git a/backend/operationtype_string.go b/backend/operationtype_string.go index 15fbba6ec..6edadb919 100644 --- a/backend/operationtype_string.go +++ b/backend/operationtype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT. +// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT package backend diff --git a/builtin/providers/oneandone/config.go b/builtin/providers/oneandone/config.go new file mode 100644 index 000000000..1192c84e7 --- /dev/null +++ b/builtin/providers/oneandone/config.go @@ -0,0 +1,24 @@ +package oneandone + +import ( + "github.com/1and1/oneandone-cloudserver-sdk-go" +) + +type Config struct { + Token string + Retries int + Endpoint string + API *oneandone.API +} + +func (c *Config) Client() (*Config, error) { + token := oneandone.SetToken(c.Token) + + if len(c.Endpoint) > 0 { + c.API = oneandone.New(token, c.Endpoint) + } else { + c.API = oneandone.New(token, oneandone.BaseUrl) + } + + return c, nil +} diff --git a/builtin/providers/oneandone/provider.go b/builtin/providers/oneandone/provider.go new file mode 100644 index 000000000..8cc65f19b --- /dev/null +++ b/builtin/providers/oneandone/provider.go @@ -0,0 +1,56 @@ +package oneandone + +import ( + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "token": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("ONEANDONE_TOKEN", nil), + Description: "1&1 token for API operations.", + }, + "retries": { + Type: schema.TypeInt, + Optional: true, + Default: 50, + DefaultFunc: schema.EnvDefaultFunc("ONEANDONE_RETRIES", nil), + }, + "endpoint": { + Type: schema.TypeString, + Optional: true, + Default: oneandone.BaseUrl, + DefaultFunc: schema.EnvDefaultFunc("ONEANDONE_ENDPOINT", nil), + }, + }, + ResourcesMap: map[string]*schema.Resource{ + "oneandone_server": resourceOneandOneServer(), + "oneandone_firewall_policy": resourceOneandOneFirewallPolicy(), + "oneandone_private_network": resourceOneandOnePrivateNetwork(), + "oneandone_public_ip": resourceOneandOnePublicIp(), + "oneandone_shared_storage": resourceOneandOneSharedStorage(), + "oneandone_monitoring_policy": resourceOneandOneMonitoringPolicy(), + "oneandone_loadbalancer": resourceOneandOneLoadbalancer(), + "oneandone_vpn": resourceOneandOneVPN(), + }, + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + var endpoint string + if d.Get("endpoint").(string) != oneandone.BaseUrl { + endpoint = d.Get("endpoint").(string) + } + config := Config{ + Token: d.Get("token").(string), + Retries: d.Get("retries").(int), + Endpoint: endpoint, + } + return config.Client() +} diff --git a/builtin/providers/oneandone/provider_test.go b/builtin/providers/oneandone/provider_test.go new file mode 100644 index 000000000..2057aac6d --- /dev/null +++ b/builtin/providers/oneandone/provider_test.go @@ -0,0 +1,36 @@ +package oneandone + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "oneandone": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("ONEANDONE_TOKEN"); v == "" { + t.Fatal("ONEANDONE_TOKEN must be set for acceptance tests") + } +} diff --git a/builtin/providers/oneandone/resource_oneandone_firewall_policy.go b/builtin/providers/oneandone/resource_oneandone_firewall_policy.go new file mode 100644 index 000000000..c62b63b5c --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_firewall_policy.go @@ -0,0 +1,274 @@ +package oneandone + +import ( + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "strings" +) + +func resourceOneandOneFirewallPolicy() *schema.Resource { + return &schema.Resource{ + + Create: resourceOneandOneFirewallCreate, + Read: resourceOneandOneFirewallRead, + Update: resourceOneandOneFirewallUpdate, + Delete: resourceOneandOneFirewallDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "rules": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Required: true, + }, + "port_from": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "port_to": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "source_ip": { + Type: schema.TypeString, + Optional: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + Required: true, + }, + }, + } +} + +func resourceOneandOneFirewallCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + req := oneandone.FirewallPolicyRequest{ + Name: d.Get("name").(string), + } + + if desc, ok := d.GetOk("description"); ok { + req.Description = desc.(string) + } + + req.Rules = getRules(d) + + fw_id, fw, err := config.API.CreateFirewallPolicy(&req) + if err != nil { + return err + } + + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + + d.SetId(fw_id) + + if err != nil { + return err + } + + return resourceOneandOneFirewallRead(d, meta) +} + +func resourceOneandOneFirewallUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + if d.HasChange("name") || d.HasChange("description") { + fw, err := config.API.UpdateFirewallPolicy(d.Id(), d.Get("name").(string), d.Get("description").(string)) + if err != nil { + return err + } + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + } + + if d.HasChange("rules") { + oldR, newR := d.GetChange("rules") + oldValues := oldR.([]interface{}) + newValues := newR.([]interface{}) + if len(oldValues) > len(newValues) { + diff := difference(oldValues, newValues) + for _, old := range diff { + o := old.(map[string]interface{}) + if o["id"] != nil { + old_id := o["id"].(string) + fw, err := config.API.DeleteFirewallPolicyRule(d.Id(), old_id) + if err != nil { + return err + } + + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + } + } + } else { + var rules []oneandone.FirewallPolicyRule + + for _, raw := range newValues { + rl := raw.(map[string]interface{}) + + if rl["id"].(string) == "" { + rule := oneandone.FirewallPolicyRule{ + Protocol: rl["protocol"].(string), + } + + if rl["port_from"] != nil { + rule.PortFrom = oneandone.Int2Pointer(rl["port_from"].(int)) + } + if rl["port_to"] != nil { + rule.PortTo = oneandone.Int2Pointer(rl["port_to"].(int)) + } + + if rl["source_ip"] != nil { + rule.SourceIp = rl["source_ip"].(string) + } + + rules = append(rules, rule) + } + } + + if len(rules) > 0 { + fw, err := config.API.AddFirewallPolicyRules(d.Id(), rules) + if err != nil { + return err + } + + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + } + } + } + + return resourceOneandOneFirewallRead(d, meta) +} + +func resourceOneandOneFirewallRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + fw, err := config.API.GetFirewallPolicy(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("rules", readRules(d, fw.Rules)) + d.Set("description", fw.Description) + + return nil +} + +func resourceOneandOneFirewallDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + fp, err := config.API.DeleteFirewallPolicy(d.Id()) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(fp) + if err != nil { + return err + } + + return nil +} + +func readRules(d *schema.ResourceData, rules []oneandone.FirewallPolicyRule) interface{} { + rawRules := d.Get("rules").([]interface{}) + counter := 0 + for _, rR := range rawRules { + if len(rules) > counter { + rawMap := rR.(map[string]interface{}) + rawMap["id"] = rules[counter].Id + if rules[counter].SourceIp != "0.0.0.0" { + rawMap["source_ip"] = rules[counter].SourceIp + } + } + counter++ + } + + return rawRules +} + +func getRules(d *schema.ResourceData) []oneandone.FirewallPolicyRule { + var rules []oneandone.FirewallPolicyRule + + if raw, ok := d.GetOk("rules"); ok { + rawRules := raw.([]interface{}) + + for _, raw := range rawRules { + rl := raw.(map[string]interface{}) + rule := oneandone.FirewallPolicyRule{ + Protocol: rl["protocol"].(string), + } + + if rl["port_from"] != nil { + rule.PortFrom = oneandone.Int2Pointer(rl["port_from"].(int)) + } + if rl["port_to"] != nil { + rule.PortTo = oneandone.Int2Pointer(rl["port_to"].(int)) + } + + if rl["source_ip"] != nil { + rule.SourceIp = rl["source_ip"].(string) + } + + rules = append(rules, rule) + } + } + return rules +} + +func difference(oldV, newV []interface{}) (toreturn []interface{}) { + var ( + lenMin int + longest []interface{} + ) + // Determine the shortest length and the longest slice + if len(oldV) < len(newV) { + lenMin = len(oldV) + longest = newV + } else { + lenMin = len(newV) + longest = oldV + } + // compare common indeces + for i := 0; i < lenMin; i++ { + if oldV[i] == nil || newV[i] == nil { + continue + } + if oldV[i].(map[string]interface{})["id"] != newV[i].(map[string]interface{})["id"] { + toreturn = append(toreturn, newV) //out += fmt.Sprintf("=>\t%s\t%s\n", oldV[i], newV[i]) + } + } + // add indeces not in common + for _, v := range longest[lenMin:] { + //out += fmt.Sprintf("=>\t%s\n", v) + toreturn = append(toreturn, v) + } + return toreturn +} diff --git a/builtin/providers/oneandone/resource_oneandone_firewall_policy_test.go b/builtin/providers/oneandone/resource_oneandone_firewall_policy_test.go new file mode 100644 index 000000000..146d63c3c --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_firewall_policy_test.go @@ -0,0 +1,178 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneFirewall_Basic(t *testing.T) { + var firewall oneandone.FirewallPolicy + + name := "test" + name_updated := "test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneFirewallDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneFirewall_basic, name), + + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneFirewallExists("oneandone_firewall_policy.fw", &firewall), + testAccCheckOneandoneFirewallAttributes("oneandone_firewall_policy.fw", name), + resource.TestCheckResourceAttr("oneandone_firewall_policy.fw", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneFirewall_update, name_updated), + + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneFirewallExists("oneandone_firewall_policy.fw", &firewall), + testAccCheckOneandoneFirewallAttributes("oneandone_firewall_policy.fw", name_updated), + resource.TestCheckResourceAttr("oneandone_firewall_policy.fw", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneFirewallDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_firewall_policy.fw" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetFirewallPolicy(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Firewall Policy still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneFirewallAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneFirewallExists(n string, fw_p *oneandone.FirewallPolicy) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_fw, err := api.GetFirewallPolicy(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching Firewall Policy: %s", rs.Primary.ID) + } + if found_fw.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + fw_p = found_fw + + return nil + } +} + +const testAccCheckOneandoneFirewall_basic = ` +resource "oneandone_firewall_policy" "fw" { + name = "%s" + rules = [ + { + "protocol" = "TCP" + "port_from" = 80 + "port_to" = 80 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "ICMP" + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 43 + "port_to" = 43 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 22 + "port_to" = 22 + "source_ip" = "0.0.0.0" + } + ] +}` + +const testAccCheckOneandoneFirewall_update = ` +resource "oneandone_firewall_policy" "fw" { + name = "%s" + rules = [ + { + "protocol" = "TCP" + "port_from" = 80 + "port_to" = 80 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "ICMP" + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 43 + "port_to" = 43 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 22 + "port_to" = 22 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 88 + "port_to" = 88 + "source_ip" = "0.0.0.0" + }, + ] +}` diff --git a/builtin/providers/oneandone/resource_oneandone_loadbalancer.go b/builtin/providers/oneandone/resource_oneandone_loadbalancer.go new file mode 100644 index 000000000..627ec51df --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_loadbalancer.go @@ -0,0 +1,370 @@ +package oneandone + +import ( + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "log" + "strings" +) + +func resourceOneandOneLoadbalancer() *schema.Resource { + return &schema.Resource{ + Create: resourceOneandOneLoadbalancerCreate, + Read: resourceOneandOneLoadbalancerRead, + Update: resourceOneandOneLoadbalancerUpdate, + Delete: resourceOneandOneLoadbalancerDelete, + Schema: map[string]*schema.Schema{ + + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "method": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateMethod, + }, + "datacenter": { + Type: schema.TypeString, + Optional: true, + }, + "persistence": { + Type: schema.TypeBool, + Optional: true, + }, + "persistence_time": { + Type: schema.TypeInt, + Optional: true, + }, + "health_check_test": { + Type: schema.TypeString, + Optional: true, + }, + "health_check_interval": { + Type: schema.TypeInt, + Optional: true, + }, + "health_check_path": { + Type: schema.TypeString, + Optional: true, + }, + "health_check_path_parser": { + Type: schema.TypeString, + Optional: true, + }, + "rules": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Required: true, + }, + "port_balancer": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "port_server": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "source_ip": { + Type: schema.TypeString, + Required: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + Required: true, + }, + }, + } +} + +func resourceOneandOneLoadbalancerCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + req := oneandone.LoadBalancerRequest{ + Name: d.Get("name").(string), + Rules: getLBRules(d), + } + + if raw, ok := d.GetOk("description"); ok { + req.Description = raw.(string) + } + + if raw, ok := d.GetOk("datacenter"); ok { + dcs, err := config.API.ListDatacenters() + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + req.DatacenterId = dc.Id + break + } + } + } + + if raw, ok := d.GetOk("method"); ok { + req.Method = raw.(string) + } + + if raw, ok := d.GetOk("persistence"); ok { + req.Persistence = oneandone.Bool2Pointer(raw.(bool)) + } + if raw, ok := d.GetOk("persistence_time"); ok { + req.PersistenceTime = oneandone.Int2Pointer(raw.(int)) + } + + if raw, ok := d.GetOk("health_check_test"); ok { + req.HealthCheckTest = raw.(string) + } + if raw, ok := d.GetOk("health_check_interval"); ok { + req.HealthCheckInterval = oneandone.Int2Pointer(raw.(int)) + } + if raw, ok := d.GetOk("health_check_path"); ok { + req.HealthCheckPath = raw.(string) + } + if raw, ok := d.GetOk("health_check_path_parser"); ok { + req.HealthCheckPathParser = raw.(string) + } + + lb_id, lb, err := config.API.CreateLoadBalancer(&req) + if err != nil { + return err + } + + err = config.API.WaitForState(lb, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + + d.SetId(lb_id) + + return resourceOneandOneLoadbalancerRead(d, meta) +} + +func getLBRules(d *schema.ResourceData) []oneandone.LoadBalancerRule { + var rules []oneandone.LoadBalancerRule + + if raw, ok := d.GetOk("rules"); ok { + rawRules := raw.([]interface{}) + log.Println("[DEBUG] raw rules:", raw) + for _, raw := range rawRules { + rl := raw.(map[string]interface{}) + rule := oneandone.LoadBalancerRule{ + Protocol: rl["protocol"].(string), + } + + if rl["port_balancer"] != nil { + rule.PortBalancer = uint16(rl["port_balancer"].(int)) + } + if rl["port_server"] != nil { + rule.PortServer = uint16(rl["port_server"].(int)) + } + + if rl["source_ip"] != nil { + rule.Source = rl["source_ip"].(string) + } + + rules = append(rules, rule) + } + } + return rules +} + +func resourceOneandOneLoadbalancerUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + if d.HasChange("name") || d.HasChange("description") || d.HasChange("method") || d.HasChange("persistence") || d.HasChange("persistence_time") || d.HasChange("health_check_test") || d.HasChange("health_check_interval") { + lb := oneandone.LoadBalancerRequest{} + if d.HasChange("name") { + _, n := d.GetChange("name") + lb.Name = n.(string) + } + if d.HasChange("description") { + _, n := d.GetChange("description") + lb.Description = n.(string) + } + if d.HasChange("method") { + _, n := d.GetChange("method") + lb.Method = (n.(string)) + } + if d.HasChange("persistence") { + _, n := d.GetChange("persistence") + lb.Persistence = oneandone.Bool2Pointer(n.(bool)) + } + if d.HasChange("persistence_time") { + _, n := d.GetChange("persistence_time") + lb.PersistenceTime = oneandone.Int2Pointer(n.(int)) + } + if d.HasChange("health_check_test") { + _, n := d.GetChange("health_check_test") + lb.HealthCheckTest = n.(string) + } + if d.HasChange("health_check_path") { + _, n := d.GetChange("health_check_path") + lb.HealthCheckPath = n.(string) + } + if d.HasChange("health_check_path_parser") { + _, n := d.GetChange("health_check_path_parser") + lb.HealthCheckPathParser = n.(string) + } + + ss, err := config.API.UpdateLoadBalancer(d.Id(), &lb) + + if err != nil { + return err + } + err = config.API.WaitForState(ss, "ACTIVE", 10, 30) + if err != nil { + return err + } + + } + + if d.HasChange("rules") { + oldR, newR := d.GetChange("rules") + oldValues := oldR.([]interface{}) + newValues := newR.([]interface{}) + if len(oldValues) > len(newValues) { + diff := difference(oldValues, newValues) + for _, old := range diff { + o := old.(map[string]interface{}) + if o["id"] != nil { + old_id := o["id"].(string) + fw, err := config.API.DeleteLoadBalancerRule(d.Id(), old_id) + if err != nil { + return err + } + + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + } + } + } else { + var rules []oneandone.LoadBalancerRule + log.Println("[DEBUG] new values:", newValues) + + for _, raw := range newValues { + rl := raw.(map[string]interface{}) + log.Println("[DEBUG] rl:", rl) + + if rl["id"].(string) == "" { + rule := oneandone.LoadBalancerRule{ + Protocol: rl["protocol"].(string), + } + + rule.PortServer = uint16(rl["port_server"].(int)) + rule.PortBalancer = uint16(rl["port_balancer"].(int)) + + rule.Source = rl["source_ip"].(string) + + log.Println("[DEBUG] adding to list", rl["protocol"], rl["source_ip"], rl["port_balancer"], rl["port_server"]) + log.Println("[DEBUG] adding to list", rule) + + rules = append(rules, rule) + } + } + + log.Println("[DEBUG] new rules:", rules) + + if len(rules) > 0 { + fw, err := config.API.AddLoadBalancerRules(d.Id(), rules) + if err != nil { + return err + } + + err = config.API.WaitForState(fw, "ACTIVE", 10, config.Retries) + } + } + } + + return resourceOneandOneLoadbalancerRead(d, meta) +} + +func resourceOneandOneLoadbalancerRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + ss, err := config.API.GetLoadBalancer(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("name", ss.Name) + d.Set("description", ss.Description) + d.Set("datacenter", ss.Datacenter.CountryCode) + d.Set("method", ss.Method) + d.Set("persistence", ss.Persistence) + d.Set("persistence_time", ss.PersistenceTime) + d.Set("health_check_test", ss.HealthCheckTest) + d.Set("health_check_interval", ss.HealthCheckInterval) + d.Set("rules", getLoadbalancerRules(ss.Rules)) + + return nil +} + +func getLoadbalancerRules(rules []oneandone.LoadBalancerRule) []map[string]interface{} { + raw := make([]map[string]interface{}, 0, len(rules)) + + for _, rule := range rules { + + toadd := map[string]interface{}{ + "id": rule.Id, + "port_balancer": rule.PortBalancer, + "port_server": rule.PortServer, + "protocol": rule.Protocol, + "source_ip": rule.Source, + } + + raw = append(raw, toadd) + } + + return raw + +} + +func resourceOneandOneLoadbalancerDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + lb, err := config.API.DeleteLoadBalancer(d.Id()) + if err != nil { + return err + } + err = config.API.WaitUntilDeleted(lb) + if err != nil { + return err + } + + return nil +} + +func validateMethod(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value != "ROUND_ROBIN" && value != "LEAST_CONNECTIONS" { + errors = append(errors, fmt.Errorf("%q value sholud be either 'ROUND_ROBIN' or 'LEAST_CONNECTIONS' not %q", k, value)) + } + + return +} diff --git a/builtin/providers/oneandone/resource_oneandone_loadbalancer_test.go b/builtin/providers/oneandone/resource_oneandone_loadbalancer_test.go new file mode 100644 index 000000000..ecd0f9443 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_loadbalancer_test.go @@ -0,0 +1,156 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneLoadbalancer_Basic(t *testing.T) { + var lb oneandone.LoadBalancer + + name := "test_loadbalancer" + name_updated := "test_loadbalancer_renamed" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneLoadbalancerDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneLoadbalancer_basic, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneLoadbalancerExists("oneandone_loadbalancer.lb", &lb), + testAccCheckOneandoneLoadbalancerAttributes("oneandone_loadbalancer.lb", name), + resource.TestCheckResourceAttr("oneandone_loadbalancer.lb", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneLoadbalancer_update, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneLoadbalancerExists("oneandone_loadbalancer.lb", &lb), + testAccCheckOneandoneLoadbalancerAttributes("oneandone_loadbalancer.lb", name_updated), + resource.TestCheckResourceAttr("oneandone_loadbalancer.lb", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneLoadbalancerDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_loadbalancer.lb" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetLoadBalancer(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Loadbalancer still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneLoadbalancerAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneLoadbalancerExists(n string, fw_p *oneandone.LoadBalancer) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_fw, err := api.GetLoadBalancer(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching Loadbalancer: %s", rs.Primary.ID) + } + if found_fw.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + fw_p = found_fw + + return nil + } +} + +const testAccCheckOneandoneLoadbalancer_basic = ` +resource "oneandone_loadbalancer" "lb" { + name = "%s" + method = "ROUND_ROBIN" + persistence = true + persistence_time = 60 + health_check_test = "TCP" + health_check_interval = 300 + datacenter = "US" + rules = [ + { + protocol = "TCP" + port_balancer = 8080 + port_server = 8089 + source_ip = "0.0.0.0" + }, + { + protocol = "TCP" + port_balancer = 9090 + port_server = 9099 + source_ip = "0.0.0.0" + } + ] +}` + +const testAccCheckOneandoneLoadbalancer_update = ` +resource "oneandone_loadbalancer" "lb" { + name = "%s" + method = "ROUND_ROBIN" + persistence = true + persistence_time = 60 + health_check_test = "TCP" + health_check_interval = 300 + datacenter = "US" + rules = [ + { + protocol = "TCP" + port_balancer = 8080 + port_server = 8089 + source_ip = "0.0.0.0" + } + ] +}` diff --git a/builtin/providers/oneandone/resource_oneandone_monitoring_policy.go b/builtin/providers/oneandone/resource_oneandone_monitoring_policy.go new file mode 100644 index 000000000..a6af20dfc --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_monitoring_policy.go @@ -0,0 +1,706 @@ +package oneandone + +import ( + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceOneandOneMonitoringPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceOneandOneMonitoringPolicyCreate, + Read: resourceOneandOneMonitoringPolicyRead, + Update: resourceOneandOneMonitoringPolicyUpdate, + Delete: resourceOneandOneMonitoringPolicyDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "email": { + Type: schema.TypeString, + Optional: true, + }, + "agent": { + Type: schema.TypeBool, + Required: true, + }, + "thresholds": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "warning": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + "critical": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + "ram": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "warning": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + "critical": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + "disk": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "warning": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + "critical": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + "transfer": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "warning": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + "critical": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + "internal_ping": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "warning": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + "critical": { + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + Required: true, + }, + "alert": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Required: true, + }, + "ports": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email_notification": { + Type: schema.TypeBool, + Required: true, + }, + "port": { + Type: schema.TypeInt, + Required: true, + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + }, + "alert_if": { + Type: schema.TypeString, + Optional: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + Optional: true, + }, + "processes": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + + "email_notification": { + Type: schema.TypeBool, + Required: true, + }, + "process": { + Type: schema.TypeString, + Required: true, + }, + "alert_if": { + Type: schema.TypeString, + Optional: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + Optional: true, + }, + }, + } +} + +func resourceOneandOneMonitoringPolicyCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + mp_request := oneandone.MonitoringPolicy{ + Name: d.Get("name").(string), + Agent: d.Get("agent").(bool), + Thresholds: getThresholds(d.Get("thresholds")), + } + + if raw, ok := d.GetOk("ports"); ok { + mp_request.Ports = getPorts(raw) + } + + if raw, ok := d.GetOk("processes"); ok { + mp_request.Processes = getProcesses(raw) + } + + mp_id, mp, err := config.API.CreateMonitoringPolicy(&mp_request) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + + d.SetId(mp_id) + + return resourceOneandOneMonitoringPolicyRead(d, meta) +} + +func resourceOneandOneMonitoringPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + req := oneandone.MonitoringPolicy{} + if d.HasChange("name") { + _, n := d.GetChange("name") + req.Name = n.(string) + } + + if d.HasChange("description") { + _, n := d.GetChange("description") + req.Description = n.(string) + } + + if d.HasChange("email") { + _, n := d.GetChange("email") + req.Email = n.(string) + } + + if d.HasChange("agent") { + _, n := d.GetChange("agent") + req.Agent = n.(bool) + } + + if d.HasChange("thresholds") { + _, n := d.GetChange("thresholds") + req.Thresholds = getThresholds(n) + } + + mp, err := config.API.UpdateMonitoringPolicy(d.Id(), &req) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + + if d.HasChange("ports") { + o, n := d.GetChange("ports") + oldValues := o.([]interface{}) + newValues := n.([]interface{}) + + if len(newValues) > len(oldValues) { + ports := getPorts(newValues) + + newports := []oneandone.MonitoringPort{} + + for _, p := range ports { + if p.Id == "" { + newports = append(newports, p) + } + } + + mp, err := config.API.AddMonitoringPolicyPorts(d.Id(), newports) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } else if len(oldValues) > len(newValues) { + diff := difference(oldValues, newValues) + ports := getPorts(diff) + + for _, port := range ports { + if port.Id == "" { + continue + } + + mp, err := config.API.DeleteMonitoringPolicyPort(d.Id(), port.Id) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } else if len(oldValues) == len(newValues) { + ports := getPorts(newValues) + + for _, port := range ports { + mp, err := config.API.ModifyMonitoringPolicyPort(d.Id(), port.Id, &port) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } + } + + if d.HasChange("processes") { + o, n := d.GetChange("processes") + oldValues := o.([]interface{}) + newValues := n.([]interface{}) + if len(newValues) > len(oldValues) { + processes := getProcesses(newValues) + + newprocesses := []oneandone.MonitoringProcess{} + + for _, p := range processes { + if p.Id == "" { + newprocesses = append(newprocesses, p) + } + } + + mp, err := config.API.AddMonitoringPolicyProcesses(d.Id(), newprocesses) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } else if len(oldValues) > len(newValues) { + diff := difference(oldValues, newValues) + processes := getProcesses(diff) + for _, process := range processes { + if process.Id == "" { + continue + } + + mp, err := config.API.DeleteMonitoringPolicyProcess(d.Id(), process.Id) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } else if len(oldValues) == len(newValues) { + processes := getProcesses(newValues) + + for _, process := range processes { + mp, err := config.API.ModifyMonitoringPolicyProcess(d.Id(), process.Id, &process) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } + } + + return resourceOneandOneMonitoringPolicyRead(d, meta) +} + +func resourceOneandOneMonitoringPolicyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + mp, err := config.API.GetMonitoringPolicy(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + if len(mp.Servers) > 0 { + } + + if len(mp.Ports) > 0 { + pports := d.Get("ports").([]interface{}) + for i, raw_ports := range pports { + port := raw_ports.(map[string]interface{}) + port["id"] = mp.Ports[i].Id + } + d.Set("ports", pports) + } + + if len(mp.Processes) > 0 { + pprocesses := d.Get("processes").([]interface{}) + for i, raw_processes := range pprocesses { + process := raw_processes.(map[string]interface{}) + process["id"] = mp.Processes[i].Id + } + d.Set("processes", pprocesses) + } + + return nil +} + +func resourceOneandOneMonitoringPolicyDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + mp, err := config.API.DeleteMonitoringPolicy(d.Id()) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(mp) + if err != nil { + return err + } + + return nil +} + +func getThresholds(d interface{}) *oneandone.MonitoringThreshold { + raw_thresholds := d.(*schema.Set).List() + + toReturn := &oneandone.MonitoringThreshold{} + + for _, thresholds := range raw_thresholds { + th_set := thresholds.(map[string]interface{}) + + //CPU + cpu_raw := th_set["cpu"].(*schema.Set) + toReturn.Cpu = &oneandone.MonitoringLevel{} + for _, c := range cpu_raw.List() { + int_k := c.(map[string]interface{}) + for _, w := range int_k["warning"].(*schema.Set).List() { + toReturn.Cpu.Warning = &oneandone.MonitoringValue{ + Value: w.(map[string]interface{})["value"].(int), + Alert: w.(map[string]interface{})["alert"].(bool), + } + } + + for _, c := range int_k["critical"].(*schema.Set).List() { + toReturn.Cpu.Critical = &oneandone.MonitoringValue{ + Value: c.(map[string]interface{})["value"].(int), + Alert: c.(map[string]interface{})["alert"].(bool), + } + } + } + //RAM + ram_raw := th_set["ram"].(*schema.Set) + toReturn.Ram = &oneandone.MonitoringLevel{} + for _, c := range ram_raw.List() { + int_k := c.(map[string]interface{}) + for _, w := range int_k["warning"].(*schema.Set).List() { + toReturn.Ram.Warning = &oneandone.MonitoringValue{ + Value: w.(map[string]interface{})["value"].(int), + Alert: w.(map[string]interface{})["alert"].(bool), + } + } + + for _, c := range int_k["critical"].(*schema.Set).List() { + toReturn.Ram.Critical = &oneandone.MonitoringValue{ + Value: c.(map[string]interface{})["value"].(int), + Alert: c.(map[string]interface{})["alert"].(bool), + } + } + } + + //DISK + disk_raw := th_set["disk"].(*schema.Set) + toReturn.Disk = &oneandone.MonitoringLevel{} + for _, c := range disk_raw.List() { + int_k := c.(map[string]interface{}) + for _, w := range int_k["warning"].(*schema.Set).List() { + toReturn.Disk.Warning = &oneandone.MonitoringValue{ + Value: w.(map[string]interface{})["value"].(int), + Alert: w.(map[string]interface{})["alert"].(bool), + } + } + + for _, c := range int_k["critical"].(*schema.Set).List() { + toReturn.Disk.Critical = &oneandone.MonitoringValue{ + Value: c.(map[string]interface{})["value"].(int), + Alert: c.(map[string]interface{})["alert"].(bool), + } + } + } + + //TRANSFER + transfer_raw := th_set["transfer"].(*schema.Set) + toReturn.Transfer = &oneandone.MonitoringLevel{} + for _, c := range transfer_raw.List() { + int_k := c.(map[string]interface{}) + for _, w := range int_k["warning"].(*schema.Set).List() { + toReturn.Transfer.Warning = &oneandone.MonitoringValue{ + Value: w.(map[string]interface{})["value"].(int), + Alert: w.(map[string]interface{})["alert"].(bool), + } + } + + for _, c := range int_k["critical"].(*schema.Set).List() { + toReturn.Transfer.Critical = &oneandone.MonitoringValue{ + Value: c.(map[string]interface{})["value"].(int), + Alert: c.(map[string]interface{})["alert"].(bool), + } + } + } + //internal ping + ping_raw := th_set["internal_ping"].(*schema.Set) + toReturn.InternalPing = &oneandone.MonitoringLevel{} + for _, c := range ping_raw.List() { + int_k := c.(map[string]interface{}) + for _, w := range int_k["warning"].(*schema.Set).List() { + toReturn.InternalPing.Warning = &oneandone.MonitoringValue{ + Value: w.(map[string]interface{})["value"].(int), + Alert: w.(map[string]interface{})["alert"].(bool), + } + } + + for _, c := range int_k["critical"].(*schema.Set).List() { + toReturn.InternalPing.Critical = &oneandone.MonitoringValue{ + Value: c.(map[string]interface{})["value"].(int), + Alert: c.(map[string]interface{})["alert"].(bool), + } + } + } + } + + return toReturn +} + +func getProcesses(d interface{}) []oneandone.MonitoringProcess { + toReturn := []oneandone.MonitoringProcess{} + + for _, raw := range d.([]interface{}) { + port := raw.(map[string]interface{}) + m_port := oneandone.MonitoringProcess{ + EmailNotification: port["email_notification"].(bool), + } + + if port["id"] != nil { + m_port.Id = port["id"].(string) + } + + if port["process"] != nil { + m_port.Process = port["process"].(string) + } + + if port["alert_if"] != nil { + m_port.AlertIf = port["alert_if"].(string) + } + + toReturn = append(toReturn, m_port) + } + + return toReturn +} + +func getPorts(d interface{}) []oneandone.MonitoringPort { + toReturn := []oneandone.MonitoringPort{} + + for _, raw := range d.([]interface{}) { + port := raw.(map[string]interface{}) + m_port := oneandone.MonitoringPort{ + EmailNotification: port["email_notification"].(bool), + Port: port["port"].(int), + } + + if port["id"] != nil { + m_port.Id = port["id"].(string) + } + + if port["protocol"] != nil { + m_port.Protocol = port["protocol"].(string) + } + + if port["alert_if"] != nil { + m_port.AlertIf = port["alert_if"].(string) + } + + toReturn = append(toReturn, m_port) + } + + return toReturn +} diff --git a/builtin/providers/oneandone/resource_oneandone_monitoring_policy_test.go b/builtin/providers/oneandone/resource_oneandone_monitoring_policy_test.go new file mode 100644 index 000000000..c6727ee21 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_monitoring_policy_test.go @@ -0,0 +1,212 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneMonitoringPolicy_Basic(t *testing.T) { + var mp oneandone.MonitoringPolicy + + name := "test" + name_updated := "test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneMonitoringPolicyDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneMonitoringPolicy_basic, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneMonitoringPolicyExists("oneandone_monitoring_policy.mp", &mp), + testAccCheckOneandoneMonitoringPolicyAttributes("oneandone_monitoring_policy.mp", name), + resource.TestCheckResourceAttr("oneandone_monitoring_policy.mp", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneMonitoringPolicy_basic, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneMonitoringPolicyExists("oneandone_monitoring_policy.mp", &mp), + testAccCheckOneandoneMonitoringPolicyAttributes("oneandone_monitoring_policy.mp", name_updated), + resource.TestCheckResourceAttr("oneandone_monitoring_policy.mp", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneMonitoringPolicyDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_monitoring_policy.mp" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetMonitoringPolicy(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("MonitoringPolicy still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneMonitoringPolicyAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneMonitoringPolicyExists(n string, fw_p *oneandone.MonitoringPolicy) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_fw, err := api.GetMonitoringPolicy(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching MonitoringPolicy: %s", rs.Primary.ID) + } + if found_fw.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + fw_p = found_fw + + return nil + } +} + +const testAccCheckOneandoneMonitoringPolicy_basic = ` +resource "oneandone_monitoring_policy" "mp" { + name = "%s" + agent = true + email = "email@address.com" + thresholds = { + cpu = { + warning = { + value = 50, + alert = false + } + critical = { + value = 66, + alert = false + } + } + ram = { + warning = { + value = 70, + alert = true + } + critical = { + value = 80, + alert = true + } + }, + ram = { + warning = { + value = 85, + alert = true + } + critical = { + value = 95, + alert = true + } + }, + disk = { + warning = { + value = 84, + alert = true + } + critical = { + value = 94, + alert = true + } + }, + transfer = { + warning = { + value = 1000, + alert = true + } + critical = { + value = 2000, + alert = true + } + }, + internal_ping = { + warning = { + value = 3000, + alert = true + } + critical = { + value = 4000, + alert = true + } + } + } + ports = [ + { + email_notification = true + port = 443 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + }, + { + email_notification = false + port = 80 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + }, + { + email_notification = true + port = 21 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + } + ] + processes = [ + { + email_notification = false + process = "httpdeamon" + alert_if = "RUNNING" + }, + { + process = "iexplorer", + alert_if = "NOT_RUNNING" + email_notification = true + }] +}` diff --git a/builtin/providers/oneandone/resource_oneandone_private_network.go b/builtin/providers/oneandone/resource_oneandone_private_network.go new file mode 100644 index 000000000..f9a4fc9e3 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_private_network.go @@ -0,0 +1,291 @@ +package oneandone + +import ( + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceOneandOnePrivateNetwork() *schema.Resource { + return &schema.Resource{ + + Create: resourceOneandOnePrivateNetworkCreate, + Read: resourceOneandOnePrivateNetworkRead, + Update: resourceOneandOnePrivateNetworkUpdate, + Delete: resourceOneandOnePrivateNetworkDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "datacenter": { + Type: schema.TypeString, + Optional: true, + }, + "network_address": { + Type: schema.TypeString, + Optional: true, + }, + "subnet_mask": { + Type: schema.TypeString, + Optional: true, + }, + "server_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + }, + } +} + +func resourceOneandOnePrivateNetworkCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + req := oneandone.PrivateNetworkRequest{ + Name: d.Get("name").(string), + } + + if raw, ok := d.GetOk("description"); ok { + req.Description = raw.(string) + } + + if raw, ok := d.GetOk("network_address"); ok { + req.NetworkAddress = raw.(string) + } + + if raw, ok := d.GetOk("subnet_mask"); ok { + req.SubnetMask = raw.(string) + } + + if raw, ok := d.GetOk("datacenter"); ok { + dcs, err := config.API.ListDatacenters() + + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + req.DatacenterId = dc.Id + break + } + } + } + + prn_id, prn, err := config.API.CreatePrivateNetwork(&req) + if err != nil { + return err + } + err = config.API.WaitForState(prn, "ACTIVE", 30, config.Retries) + + if err != nil { + return err + } + + d.SetId(prn_id) + + var ids []string + if raw, ok := d.GetOk("server_ids"); ok { + + rawIps := raw.(*schema.Set).List() + + for _, raw := range rawIps { + ids = append(ids, raw.(string)) + server, err := config.API.ShutdownServer(raw.(string), false) + if err != nil { + return err + } + err = config.API.WaitForState(server, "POWERED_OFF", 10, config.Retries) + if err != nil { + return err + } + + } + } + + prn, err = config.API.AttachPrivateNetworkServers(d.Id(), ids) + if err != nil { + return err + } + + err = config.API.WaitForState(prn, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + + for _, id := range ids { + server, err := config.API.StartServer(id) + if err != nil { + return err + } + + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + if err != nil { + return err + } + } + + return resourceOneandOnePrivateNetworkRead(d, meta) +} + +func resourceOneandOnePrivateNetworkUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + if d.HasChange("name") || d.HasChange("description") || d.HasChange("network_address") || d.HasChange("subnet_mask") { + pnset := oneandone.PrivateNetworkRequest{} + + pnset.Name = d.Get("name").(string) + + pnset.Description = d.Get("description").(string) + pnset.NetworkAddress = d.Get("network_address").(string) + pnset.SubnetMask = d.Get("subnet_mask").(string) + + prn, err := config.API.UpdatePrivateNetwork(d.Id(), &pnset) + + if err != nil { + return err + } + + err = config.API.WaitForState(prn, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + + if d.HasChange("server_ids") { + o, n := d.GetChange("server_ids") + + newValues := n.(*schema.Set).List() + oldValues := o.(*schema.Set).List() + + var ids []string + for _, newV := range oldValues { + ids = append(ids, newV.(string)) + } + for _, id := range ids { + server, err := config.API.ShutdownServer(id, false) + if err != nil { + return err + } + err = config.API.WaitForState(server, "POWERED_OFF", 10, config.Retries) + if err != nil { + return err + } + + _, err = config.API.RemoveServerPrivateNetwork(id, d.Id()) + if err != nil { + return err + } + + prn, _ := config.API.GetPrivateNetwork(d.Id()) + + err = config.API.WaitForState(prn, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + + } + + var newids []string + + for _, newV := range newValues { + newids = append(newids, newV.(string)) + } + pn, err := config.API.AttachPrivateNetworkServers(d.Id(), newids) + + if err != nil { + return err + } + err = config.API.WaitForState(pn, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + + for _, id := range newids { + server, err := config.API.StartServer(id) + if err != nil { + return err + } + + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + if err != nil { + return err + } + } + } + + return resourceOneandOnePrivateNetworkRead(d, meta) +} + +func resourceOneandOnePrivateNetworkRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + pn, err := config.API.GetPrivateNetwork(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("name", pn.Name) + d.Set("description", pn.Description) + d.Set("network_address", pn.NetworkAddress) + d.Set("subnet_mask", pn.SubnetMask) + d.Set("datacenter", pn.Datacenter.CountryCode) + + var toAdd []string + for _, s := range pn.Servers { + toAdd = append(toAdd, s.Id) + } + d.Set("server_ids", toAdd) + return nil +} + +func resourceOneandOnePrivateNetworkDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + pn, err := config.API.GetPrivateNetwork(d.Id()) + + for _, server := range pn.Servers { + srv, err := config.API.ShutdownServer(server.Id, false) + if err != nil { + return err + } + err = config.API.WaitForState(srv, "POWERED_OFF", 10, config.Retries) + if err != nil { + return err + } + } + + pn, err = config.API.DeletePrivateNetwork(d.Id()) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(pn) + if err != nil { + return err + } + + for _, server := range pn.Servers { + srv, err := config.API.StartServer(server.Id) + if err != nil { + return err + } + err = config.API.WaitForState(srv, "POWERED_ON", 10, config.Retries) + if err != nil { + return err + } + } + + return nil +} diff --git a/builtin/providers/oneandone/resource_oneandone_private_network_test.go b/builtin/providers/oneandone/resource_oneandone_private_network_test.go new file mode 100644 index 000000000..e91da76f2 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_private_network_test.go @@ -0,0 +1,160 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandonePrivateNetwork_Basic(t *testing.T) { + var net oneandone.PrivateNetwork + + name := "test" + name_updated := "test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOneandonePrivateNetworkDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandonePrivateNetwork_basic, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandonePrivateNetworkExists("oneandone_private_network.pn", &net), + testAccCheckOneandonePrivateNetworkAttributes("oneandone_private_network.pn", name), + resource.TestCheckResourceAttr("oneandone_private_network.pn", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandonePrivateNetwork_basic, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandonePrivateNetworkExists("oneandone_private_network.pn", &net), + testAccCheckOneandonePrivateNetworkAttributes("oneandone_private_network.pn", name_updated), + resource.TestCheckResourceAttr("oneandone_private_network.pn", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckOneandonePrivateNetworkDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_private_network" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetPrivateNetwork(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("PrivateNetwork still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandonePrivateNetworkAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandonePrivateNetworkExists(n string, server *oneandone.PrivateNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_server, err := api.GetPrivateNetwork(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching PrivateNetwork: %s", rs.Primary.ID) + } + if found_server.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + server = found_server + + return nil + } +} + +const testAccCheckOneandonePrivateNetwork_basic = ` +resource "oneandone_server" "server1" { + name = "server_private_net_01" + description = "ttt" + image = "CoreOS_Stable_64std" + datacenter = "US" + vcores = 1 + cores_per_processor = 1 + ram = 2 + password = "Kv40kd8PQb" + hdds = [ + { + disk_size = 60 + is_main = true + } + ] +} + +resource "oneandone_server" "server2" { + name = "server_private_net_02" + description = "ttt" + image = "CoreOS_Stable_64std" + datacenter = "US" + vcores = 1 + cores_per_processor = 1 + ram = 2 + password = "${oneandone_server.server1.password}" + hdds = [ + { + disk_size = 60 + is_main = true + } + ] +} + +resource "oneandone_private_network" "pn" { + name = "%s", + description = "new private net" + datacenter = "US" + network_address = "192.168.7.0" + subnet_mask = "255.255.255.0" + server_ids = [ + "${oneandone_server.server1.id}", + "${oneandone_server.server2.id}" + ] +} +` diff --git a/builtin/providers/oneandone/resource_oneandone_public_ip.go b/builtin/providers/oneandone/resource_oneandone_public_ip.go new file mode 100644 index 000000000..2c1bec240 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_public_ip.go @@ -0,0 +1,133 @@ +package oneandone + +import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceOneandOnePublicIp() *schema.Resource { + return &schema.Resource{ + + Create: resourceOneandOnePublicIpCreate, + Read: resourceOneandOnePublicIpRead, + Update: resourceOneandOnePublicIpUpdate, + Delete: resourceOneandOnePublicIpDelete, + Schema: map[string]*schema.Schema{ + "ip_type": { //IPV4 or IPV6 + Type: schema.TypeString, + Required: true, + }, + "reverse_dns": { + Type: schema.TypeString, + Optional: true, + }, + "datacenter": { + Type: schema.TypeString, + Optional: true, + }, + "ip_address": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceOneandOnePublicIpCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + var reverse_dns string + var datacenter_id string + + if raw, ok := d.GetOk("reverse_dns"); ok { + reverse_dns = raw.(string) + } + + if raw, ok := d.GetOk("datacenter"); ok { + dcs, err := config.API.ListDatacenters() + + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + datacenter_id = dc.Id + break + } + } + + } + + ip_id, ip, err := config.API.CreatePublicIp(d.Get("ip_type").(string), reverse_dns, datacenter_id) + if err != nil { + return err + } + + err = config.API.WaitForState(ip, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + d.SetId(ip_id) + + return resourceOneandOnePublicIpRead(d, meta) +} + +func resourceOneandOnePublicIpRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + ip, err := config.API.GetPublicIp(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("ip_address", ip.IpAddress) + d.Set("revers_dns", ip.ReverseDns) + d.Set("datacenter", ip.Datacenter.CountryCode) + d.Set("ip_type", ip.Type) + + return nil +} + +func resourceOneandOnePublicIpUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + if d.HasChange("reverse_dns") { + _, n := d.GetChange("reverse_dns") + ip, err := config.API.UpdatePublicIp(d.Id(), n.(string)) + if err != nil { + return err + } + + err = config.API.WaitForState(ip, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + } + + return resourceOneandOnePublicIpRead(d, meta) +} + +func resourceOneandOnePublicIpDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + ip, err := config.API.DeletePublicIp(d.Id()) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(ip) + if err != nil { + + return err + } + + return nil +} diff --git a/builtin/providers/oneandone/resource_oneandone_public_ip_test.go b/builtin/providers/oneandone/resource_oneandone_public_ip_test.go new file mode 100644 index 000000000..c797dc666 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_public_ip_test.go @@ -0,0 +1,119 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandonePublicIp_Basic(t *testing.T) { + var public_ip oneandone.PublicIp + + reverse_dns := "example.de" + reverse_dns_updated := "example.ba" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandonePublicIpDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandonePublicIp_basic, reverse_dns), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandonePublicIpExists("oneandone_public_ip.ip", &public_ip), + testAccCheckOneandonePublicIpAttributes("oneandone_public_ip.ip", reverse_dns), + resource.TestCheckResourceAttr("oneandone_public_ip.ip", "reverse_dns", reverse_dns), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandonePublicIp_basic, reverse_dns_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandonePublicIpExists("oneandone_public_ip.ip", &public_ip), + testAccCheckOneandonePublicIpAttributes("oneandone_public_ip.ip", reverse_dns_updated), + resource.TestCheckResourceAttr("oneandone_public_ip.ip", "reverse_dns", reverse_dns_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandonePublicIpDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_public_ip" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetPublicIp(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Public IP still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandonePublicIpAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["reverse_dns"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandonePublicIpExists(n string, public_ip *oneandone.PublicIp) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_public_ip, err := api.GetPublicIp(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching public IP: %s", rs.Primary.ID) + } + if found_public_ip.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + public_ip = found_public_ip + + return nil + } +} + +const testAccCheckOneandonePublicIp_basic = ` +resource "oneandone_public_ip" "ip" { + "ip_type" = "IPV4" + "reverse_dns" = "%s" + "datacenter" = "GB" +}` diff --git a/builtin/providers/oneandone/resource_oneandone_server.go b/builtin/providers/oneandone/resource_oneandone_server.go new file mode 100644 index 000000000..930aba41a --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_server.go @@ -0,0 +1,562 @@ +package oneandone + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "golang.org/x/crypto/ssh" + "io/ioutil" + "log" + "strings" + + "errors" +) + +func resourceOneandOneServer() *schema.Resource { + return &schema.Resource{ + Create: resourceOneandOneServerCreate, + Read: resourceOneandOneServerRead, + Update: resourceOneandOneServerUpdate, + Delete: resourceOneandOneServerDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "image": { + Type: schema.TypeString, + Required: true, + }, + "vcores": { + Type: schema.TypeInt, + Required: true, + }, + "cores_per_processor": { + Type: schema.TypeInt, + Required: true, + }, + "ram": { + Type: schema.TypeFloat, + Required: true, + }, + "ssh_key_path": { + Type: schema.TypeString, + Optional: true, + }, + "password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "datacenter": { + Type: schema.TypeString, + Optional: true, + }, + "ip": { + Type: schema.TypeString, + Optional: true, + }, + "ips": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "ip": { + Type: schema.TypeString, + Computed: true, + }, + "firewall_policy_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Computed: true, + }, + "hdds": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "disk_size": { + Type: schema.TypeInt, + Required: true, + }, + "is_main": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + Required: true, + }, + "firewall_policy_id": { + Type: schema.TypeString, + Optional: true, + }, + "monitoring_policy_id": { + Type: schema.TypeString, + Optional: true, + }, + "loadbalancer_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceOneandOneServerCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + saps, _ := config.API.ListServerAppliances() + + var sa oneandone.ServerAppliance + for _, a := range saps { + + if a.Type == "IMAGE" && strings.Contains(strings.ToLower(a.Name), strings.ToLower(d.Get("image").(string))) { + sa = a + break + } + } + + var hdds []oneandone.Hdd + if raw, ok := d.GetOk("hdds"); ok { + rawhdds := raw.([]interface{}) + + var istheremain bool + for _, raw := range rawhdds { + hd := raw.(map[string]interface{}) + hdd := oneandone.Hdd{ + Size: hd["disk_size"].(int), + IsMain: hd["is_main"].(bool), + } + + if hdd.IsMain { + if hdd.Size < sa.MinHddSize { + return fmt.Errorf(fmt.Sprintf("Minimum required disk size %d", sa.MinHddSize)) + } + istheremain = true + } + + hdds = append(hdds, hdd) + } + + if !istheremain { + return fmt.Errorf("At least one HDD has to be %s", "`is_main`") + } + } + + req := oneandone.ServerRequest{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + ApplianceId: sa.Id, + PowerOn: true, + Hardware: oneandone.Hardware{ + Vcores: d.Get("vcores").(int), + CoresPerProcessor: d.Get("cores_per_processor").(int), + Ram: float32(d.Get("ram").(float64)), + Hdds: hdds, + }, + } + + if raw, ok := d.GetOk("ip"); ok { + + new_ip := raw.(string) + + ips, err := config.API.ListPublicIps() + if err != nil { + return err + } + + for _, ip := range ips { + if ip.IpAddress == new_ip { + req.IpId = ip.Id + break + } + } + + log.Println("[DEBUG] req.IP", req.IpId) + } + + if raw, ok := d.GetOk("datacenter"); ok { + + dcs, err := config.API.ListDatacenters() + + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + req.DatacenterId = dc.Id + break + } + } + } + + if fwp_id, ok := d.GetOk("firewall_policy_id"); ok { + req.FirewallPolicyId = fwp_id.(string) + } + + if mp_id, ok := d.GetOk("monitoring_policy_id"); ok { + req.MonitoringPolicyId = mp_id.(string) + } + + if mp_id, ok := d.GetOk("loadbalancer_id"); ok { + req.LoadBalancerId = mp_id.(string) + } + + var privateKey string + if raw, ok := d.GetOk("ssh_key_path"); ok { + rawpath := raw.(string) + + priv, publicKey, err := getSshKey(rawpath) + privateKey = priv + if err != nil { + return err + } + + req.SSHKey = publicKey + } + + var password string + if raw, ok := d.GetOk("password"); ok { + req.Password = raw.(string) + password = req.Password + } + + server_id, server, err := config.API.CreateServer(&req) + if err != nil { + return err + } + + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + + d.SetId(server_id) + server, err = config.API.GetServer(d.Id()) + if err != nil { + return err + } + + if password == "" { + password = server.FirstPassword + } + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": server.Ips[0].Ip, + "password": password, + "private_key": privateKey, + }) + + return resourceOneandOneServerRead(d, meta) +} + +func resourceOneandOneServerRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + server, err := config.API.GetServer(d.Id()) + + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("name", server.Name) + d.Set("datacenter", server.Datacenter.CountryCode) + + d.Set("hdds", readHdds(server.Hardware)) + + d.Set("ips", readIps(server.Ips)) + + if len(server.FirstPassword) > 0 { + d.Set("password", server.FirstPassword) + } + + return nil +} + +func resourceOneandOneServerUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + if d.HasChange("name") || d.HasChange("description") { + _, name := d.GetChange("name") + _, description := d.GetChange("description") + server, err := config.API.RenameServer(d.Id(), name.(string), description.(string)) + if err != nil { + return err + } + + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + + } + + if d.HasChange("hdds") { + oldV, newV := d.GetChange("hdds") + newValues := newV.([]interface{}) + oldValues := oldV.([]interface{}) + + if len(oldValues) > len(newValues) { + diff := difference(oldValues, newValues) + for _, old := range diff { + o := old.(map[string]interface{}) + old_id := o["id"].(string) + server, err := config.API.DeleteServerHdd(d.Id(), old_id) + if err != nil { + return err + } + + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + if err != nil { + return err + } + } + } else { + for _, newHdd := range newValues { + n := newHdd.(map[string]interface{}) + //old := oldHdd.(map[string]interface{}) + + if n["id"].(string) == "" { + hdds := oneandone.ServerHdds{ + Hdds: []oneandone.Hdd{ + { + Size: n["disk_size"].(int), + IsMain: n["is_main"].(bool), + }, + }, + } + + server, err := config.API.AddServerHdds(d.Id(), &hdds) + + if err != nil { + return err + } + err = config.API.WaitForState(server, "POWERED_ON", 10, config.Retries) + if err != nil { + return err + } + } else { + id := n["id"].(string) + isMain := n["is_main"].(bool) + + if id != "" && !isMain { + log.Println("[DEBUG] Resizing existing HDD") + config.API.ResizeServerHdd(d.Id(), id, n["disk_size"].(int)) + } + } + + } + } + } + + if d.HasChange("monitoring_policy_id") { + o, n := d.GetChange("monitoring_policy_id") + + if n == nil { + mp, err := config.API.RemoveMonitoringPolicyServer(o.(string), d.Id()) + + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } else { + mp, err := config.API.AttachMonitoringPolicyServers(n.(string), []string{d.Id()}) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } + + if d.HasChange("loadbalancer_id") { + o, n := d.GetChange("loadbalancer_id") + server, err := config.API.GetServer(d.Id()) + if err != nil { + return err + } + + if n == nil || n.(string) == "" { + log.Println("[DEBUG] Removing") + log.Println("[DEBUG] IPS:", server.Ips) + + for _, ip := range server.Ips { + mp, err := config.API.DeleteLoadBalancerServerIp(o.(string), ip.Id) + + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } else { + log.Println("[DEBUG] Adding") + ip_ids := []string{} + for _, ip := range server.Ips { + ip_ids = append(ip_ids, ip.Id) + } + mp, err := config.API.AddLoadBalancerServerIps(n.(string), ip_ids) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + + } + } + + if d.HasChange("firewall_policy_id") { + server, err := config.API.GetServer(d.Id()) + if err != nil { + return err + } + + o, n := d.GetChange("firewall_policy_id") + if n == nil { + for _, ip := range server.Ips { + mp, err := config.API.DeleteFirewallPolicyServerIp(o.(string), ip.Id) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } else { + ip_ids := []string{} + for _, ip := range server.Ips { + ip_ids = append(ip_ids, ip.Id) + } + + mp, err := config.API.AddFirewallPolicyServerIps(n.(string), ip_ids) + if err != nil { + return err + } + + err = config.API.WaitForState(mp, "ACTIVE", 30, config.Retries) + if err != nil { + return err + } + } + } + + return resourceOneandOneServerRead(d, meta) +} + +func resourceOneandOneServerDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + _, ok := d.GetOk("ip") + + server, err := config.API.DeleteServer(d.Id(), ok) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(server) + + if err != nil { + log.Println("[DEBUG] ************ ERROR While waiting ************") + return err + } + return nil +} + +func readHdds(hardware *oneandone.Hardware) []map[string]interface{} { + hdds := make([]map[string]interface{}, 0, len(hardware.Hdds)) + + for _, hd := range hardware.Hdds { + hdds = append(hdds, map[string]interface{}{ + "id": hd.Id, + "disk_size": hd.Size, + "is_main": hd.IsMain, + }) + } + + return hdds +} + +func readIps(ips []oneandone.ServerIp) []map[string]interface{} { + raw := make([]map[string]interface{}, 0, len(ips)) + for _, ip := range ips { + + toadd := map[string]interface{}{ + "ip": ip.Ip, + "id": ip.Id, + } + + if ip.Firewall != nil { + toadd["firewall_policy_id"] = ip.Firewall.Id + } + raw = append(raw, toadd) + } + + return raw +} + +func getSshKey(path string) (privatekey string, publickey string, err error) { + pemBytes, err := ioutil.ReadFile(path) + + if err != nil { + return "", "", err + } + + block, _ := pem.Decode(pemBytes) + + if block == nil { + return "", "", errors.New("File " + path + " contains nothing") + } + + priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) + + if err != nil { + return "", "", err + } + + priv_blk := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: x509.MarshalPKCS1PrivateKey(priv), + } + + pub, err := ssh.NewPublicKey(&priv.PublicKey) + if err != nil { + return "", "", err + } + publickey = string(ssh.MarshalAuthorizedKey(pub)) + privatekey = string(pem.EncodeToMemory(&priv_blk)) + + return privatekey, publickey, nil +} diff --git a/builtin/providers/oneandone/resource_oneandone_server_test.go b/builtin/providers/oneandone/resource_oneandone_server_test.go new file mode 100644 index 000000000..ed643abfa --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_server_test.go @@ -0,0 +1,130 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneServer_Basic(t *testing.T) { + var server oneandone.Server + + name := "test_server" + name_updated := "test_server_renamed" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneServerDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneServer_basic, name, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneServerExists("oneandone_server.server", &server), + testAccCheckOneandoneServerAttributes("oneandone_server.server", name), + resource.TestCheckResourceAttr("oneandone_server.server", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneServer_basic, name_updated, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneServerExists("oneandone_server.server", &server), + testAccCheckOneandoneServerAttributes("oneandone_server.server", name_updated), + resource.TestCheckResourceAttr("oneandone_server.server", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneServerDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_server" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetServer(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Server still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneServerAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneServerExists(n string, server *oneandone.Server) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_server, err := api.GetServer(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching Server: %s", rs.Primary.ID) + } + if found_server.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + server = found_server + + return nil + } +} + +const testAccCheckOneandoneServer_basic = ` +resource "oneandone_server" "server" { + name = "%s" + description = "%s" + image = "ubuntu" + datacenter = "GB" + vcores = 1 + cores_per_processor = 1 + ram = 2 + password = "Kv40kd8PQb" + hdds = [ + { + disk_size = 20 + is_main = true + } + ] +}` diff --git a/builtin/providers/oneandone/resource_oneandone_vpn.go b/builtin/providers/oneandone/resource_oneandone_vpn.go new file mode 100644 index 000000000..865c3361a --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_vpn.go @@ -0,0 +1,217 @@ +package oneandone + +import ( + "crypto/md5" + "encoding/base64" + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "io" + "os" + fp "path/filepath" + "strings" +) + +func resourceOneandOneVPN() *schema.Resource { + return &schema.Resource{ + Create: resourceOneandOneVPNCreate, + Read: resourceOneandOneVPNRead, + Update: resourceOneandOneVPNUpdate, + Delete: resourceOneandOneVPNDelete, + Schema: map[string]*schema.Schema{ + + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "download_path": { + Type: schema.TypeString, + Computed: true, + }, + "datacenter": { + Type: schema.TypeString, + Optional: true, + }, + "file_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceOneandOneVPNCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + var datacenter string + + if raw, ok := d.GetOk("datacenter"); ok { + dcs, err := config.API.ListDatacenters() + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + datacenter = dc.Id + break + } + } + } + + var description string + if raw, ok := d.GetOk("description"); ok { + description = raw.(string) + } + + vpn_id, vpn, err := config.API.CreateVPN(d.Get("name").(string), description, datacenter) + if err != nil { + return err + } + + err = config.API.WaitForState(vpn, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + + d.SetId(vpn_id) + + return resourceOneandOneVPNRead(d, meta) +} + +func resourceOneandOneVPNUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + if d.HasChange("name") || d.HasChange("description") { + + vpn, err := config.API.ModifyVPN(d.Id(), d.Get("name").(string), d.Get("description").(string)) + if err != nil { + return err + } + + err = config.API.WaitForState(vpn, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + } + + return resourceOneandOneVPNRead(d, meta) +} + +func resourceOneandOneVPNRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + vpn, err := config.API.GetVPN(d.Id()) + + base64_str, err := config.API.GetVPNConfigFile(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + var download_path string + if raw, ok := d.GetOk("download_path"); ok { + download_path = raw.(string) + } + + path, fileName, err := writeCofnig(vpn, download_path, base64_str) + if err != nil { + return err + } + + d.Set("name", vpn.Name) + d.Set("description", vpn.Description) + d.Set("download_path", path) + d.Set("file_name", fileName) + d.Set("datacenter", vpn.Datacenter.CountryCode) + + return nil +} + +func writeCofnig(vpn *oneandone.VPN, path, base64config string) (string, string, error) { + data, err := base64.StdEncoding.DecodeString(base64config) + if err != nil { + return "", "", err + } + + var fileName string + if vpn.CloudPanelId != "" { + fileName = vpn.CloudPanelId + ".zip" + } else { + fileName = "vpn_" + fmt.Sprintf("%x", md5.Sum(data)) + ".zip" + } + + if path == "" { + path, err = os.Getwd() + if err != nil { + return "", "", err + } + } + + if !fp.IsAbs(path) { + path, err = fp.Abs(path) + if err != nil { + return "", "", err + } + } + + _, err = os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + // make all dirs + os.MkdirAll(path, 0666) + } else { + return "", "", err + } + } + + fpath := fp.Join(path, fileName) + + f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0666) + defer f.Close() + + if err != nil { + return "", "", err + } + + n, err := f.Write(data) + if err == nil && n < len(data) { + err = io.ErrShortWrite + } + + if err != nil { + return "", "", err + } + + return path, fileName, nil + +} + +func resourceOneandOneVPNDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + vpn, err := config.API.DeleteVPN(d.Id()) + if err != nil { + return err + } + + err = config.API.WaitUntilDeleted(vpn) + if err != nil { + return err + } + + fullPath := fp.Join(d.Get("download_path").(string), d.Get("file_name").(string)) + if _, err := os.Stat(fullPath); !os.IsNotExist(err) { + os.Remove(fullPath) + } + + return nil +} diff --git a/builtin/providers/oneandone/resource_oneandone_vpn_test.go b/builtin/providers/oneandone/resource_oneandone_vpn_test.go new file mode 100644 index 000000000..94e84bb61 --- /dev/null +++ b/builtin/providers/oneandone/resource_oneandone_vpn_test.go @@ -0,0 +1,119 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneVpn_Basic(t *testing.T) { + var server oneandone.VPN + + name := "test" + name_updated := "test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneVPNDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneVPN_basic, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneVPNExists("oneandone_vpn.vpn", &server), + testAccCheckOneandoneVPNAttributes("oneandone_vpn.vpn", name), + resource.TestCheckResourceAttr("oneandone_vpn.vpn", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneVPN_basic, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneVPNExists("oneandone_vpn.vpn", &server), + testAccCheckOneandoneVPNAttributes("oneandone_vpn.vpn", name_updated), + resource.TestCheckResourceAttr("oneandone_vpn.vpn", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneVPNDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_server" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetVPN(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("VPN still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneVPNAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneVPNExists(n string, server *oneandone.VPN) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_server, err := api.GetVPN(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching VPN: %s", rs.Primary.ID) + } + if found_server.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + server = found_server + + return nil + } +} + +const testAccCheckOneandoneVPN_basic = ` +resource "oneandone_vpn" "vpn" { + datacenter = "GB" + name = "%s" + description = "ttest descr" +}` diff --git a/builtin/providers/oneandone/resources_oneandone_shared_storage.go b/builtin/providers/oneandone/resources_oneandone_shared_storage.go new file mode 100644 index 000000000..f690e0cf6 --- /dev/null +++ b/builtin/providers/oneandone/resources_oneandone_shared_storage.go @@ -0,0 +1,256 @@ +package oneandone + +import ( + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/schema" + "strings" +) + +func resourceOneandOneSharedStorage() *schema.Resource { + return &schema.Resource{ + Create: resourceOneandOneSharedStorageCreate, + Read: resourceOneandOneSharedStorageRead, + Update: resourceOneandOneSharedStorageUpdate, + Delete: resourceOneandOneSharedStorageDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "size": { + Type: schema.TypeInt, + Required: true, + }, + "datacenter": { + Type: schema.TypeString, + Required: true, + }, + "storage_servers": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + }, + "rights": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + Optional: true, + }, + }, + } +} + +func resourceOneandOneSharedStorageCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + req := oneandone.SharedStorageRequest{ + Name: d.Get("name").(string), + Size: oneandone.Int2Pointer(d.Get("size").(int)), + } + + if raw, ok := d.GetOk("description"); ok { + req.Description = raw.(string) + + } + + if raw, ok := d.GetOk("datacenter"); ok { + dcs, err := config.API.ListDatacenters() + + if err != nil { + return fmt.Errorf("An error occured while fetching list of datacenters %s", err) + + } + + decenter := raw.(string) + for _, dc := range dcs { + if strings.ToLower(dc.CountryCode) == strings.ToLower(decenter) { + req.DatacenterId = dc.Id + break + } + } + } + + ss_id, ss, err := config.API.CreateSharedStorage(&req) + if err != nil { + return err + } + + err = config.API.WaitForState(ss, "ACTIVE", 10, config.Retries) + if err != nil { + return err + } + d.SetId(ss_id) + + if raw, ok := d.GetOk("storage_servers"); ok { + + storage_servers := []oneandone.SharedStorageServer{} + + rawRights := raw.([]interface{}) + for _, raws_ss := range rawRights { + ss := raws_ss.(map[string]interface{}) + storage_server := oneandone.SharedStorageServer{ + Id: ss["id"].(string), + Rights: ss["rights"].(string), + } + storage_servers = append(storage_servers, storage_server) + } + + ss, err := config.API.AddSharedStorageServers(ss_id, storage_servers) + + if err != nil { + return err + } + + err = config.API.WaitForState(ss, "ACTIVE", 10, 30) + if err != nil { + return err + } + } + + return resourceOneandOneSharedStorageRead(d, meta) +} + +func resourceOneandOneSharedStorageUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + if d.HasChange("name") || d.HasChange("description") || d.HasChange("size") { + ssu := oneandone.SharedStorageRequest{} + if d.HasChange("name") { + _, n := d.GetChange("name") + ssu.Name = n.(string) + } + if d.HasChange("description") { + _, n := d.GetChange("description") + ssu.Description = n.(string) + } + if d.HasChange("size") { + _, n := d.GetChange("size") + ssu.Size = oneandone.Int2Pointer(n.(int)) + } + + ss, err := config.API.UpdateSharedStorage(d.Id(), &ssu) + + if err != nil { + return err + } + err = config.API.WaitForState(ss, "ACTIVE", 10, 30) + if err != nil { + return err + } + + } + + if d.HasChange("storage_servers") { + + o, n := d.GetChange("storage_servers") + + oldV := o.([]interface{}) + + for _, old := range oldV { + ol := old.(map[string]interface{}) + + ss, err := config.API.DeleteSharedStorageServer(d.Id(), ol["id"].(string)) + if err != nil { + return err + } + + err = config.API.WaitForState(ss, "ACTIVE", 10, config.Retries) + + if err != nil { + return err + } + + } + + newV := n.([]interface{}) + + ids := []oneandone.SharedStorageServer{} + for _, newValue := range newV { + nn := newValue.(map[string]interface{}) + ids = append(ids, oneandone.SharedStorageServer{ + Id: nn["id"].(string), + Rights: nn["rights"].(string), + }) + } + + if len(ids) > 0 { + ss, err := config.API.AddSharedStorageServers(d.Id(), ids) + if err != nil { + return err + } + + err = config.API.WaitForState(ss, "ACTIVE", 10, config.Retries) + + if err != nil { + return err + } + } + + //DeleteSharedStorageServer + + } + + return resourceOneandOneSharedStorageRead(d, meta) +} + +func resourceOneandOneSharedStorageRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + ss, err := config.API.GetSharedStorage(d.Id()) + if err != nil { + if strings.Contains(err.Error(), "404") { + d.SetId("") + return nil + } + return err + } + + d.Set("name", ss.Name) + d.Set("description", ss.Description) + d.Set("size", ss.Size) + d.Set("datacenter", ss.Datacenter.CountryCode) + d.Set("storage_servers", getStorageServers(ss.Servers)) + + return nil +} + +func getStorageServers(servers []oneandone.SharedStorageServer) []map[string]interface{} { + raw := make([]map[string]interface{}, 0, len(servers)) + + for _, server := range servers { + + toadd := map[string]interface{}{ + "id": server.Id, + "rights": server.Rights, + } + + raw = append(raw, toadd) + } + + return raw + +} +func resourceOneandOneSharedStorageDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + ss, err := config.API.DeleteSharedStorage(d.Id()) + if err != nil { + return err + } + err = config.API.WaitUntilDeleted(ss) + if err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/oneandone/resources_oneandone_shared_storage_test.go b/builtin/providers/oneandone/resources_oneandone_shared_storage_test.go new file mode 100644 index 000000000..dcc07302a --- /dev/null +++ b/builtin/providers/oneandone/resources_oneandone_shared_storage_test.go @@ -0,0 +1,120 @@ +package oneandone + +import ( + "fmt" + "testing" + + "github.com/1and1/oneandone-cloudserver-sdk-go" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "os" + "time" +) + +func TestAccOneandoneSharedStorage_Basic(t *testing.T) { + var storage oneandone.SharedStorage + + name := "test_storage" + name_updated := "test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDOneandoneSharedStorageDestroyCheck, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneSharedStorage_basic, name), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneSharedStorageExists("oneandone_shared_storage.storage", &storage), + testAccCheckOneandoneSharedStorageAttributes("oneandone_shared_storage.storage", name), + resource.TestCheckResourceAttr("oneandone_shared_storage.storage", "name", name), + ), + }, + resource.TestStep{ + Config: fmt.Sprintf(testAccCheckOneandoneSharedStorage_basic, name_updated), + Check: resource.ComposeTestCheckFunc( + func(*terraform.State) error { + time.Sleep(10 * time.Second) + return nil + }, + testAccCheckOneandoneSharedStorageExists("oneandone_shared_storage.storage", &storage), + testAccCheckOneandoneSharedStorageAttributes("oneandone_shared_storage.storage", name_updated), + resource.TestCheckResourceAttr("oneandone_shared_storage.storage", "name", name_updated), + ), + }, + }, + }) +} + +func testAccCheckDOneandoneSharedStorageDestroyCheck(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "oneandone_shared_storage" { + continue + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + _, err := api.GetVPN(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("VPN still exists %s %s", rs.Primary.ID, err.Error()) + } + } + + return nil +} +func testAccCheckOneandoneSharedStorageAttributes(n string, reverse_dns string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.Attributes["name"] != reverse_dns { + return fmt.Errorf("Bad name: expected %s : found %s ", reverse_dns, rs.Primary.Attributes["name"]) + } + + return nil + } +} + +func testAccCheckOneandoneSharedStorageExists(n string, storage *oneandone.SharedStorage) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + api := oneandone.New(os.Getenv("ONEANDONE_TOKEN"), oneandone.BaseUrl) + + found_storage, err := api.GetSharedStorage(rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error occured while fetching SharedStorage: %s", rs.Primary.ID) + } + if found_storage.Id != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + storage = found_storage + + return nil + } +} + +const testAccCheckOneandoneSharedStorage_basic = ` +resource "oneandone_shared_storage" "storage" { + name = "%s" + description = "ttt" + size = 50 + datacenter = "GB" +}` diff --git a/command/counthookaction_string.go b/command/counthookaction_string.go index 423cae07e..c0c40d0de 100644 --- a/command/counthookaction_string.go +++ b/command/counthookaction_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT. +// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT package command diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index 9e53c16b9..dfb2b9f1d 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -47,6 +47,7 @@ import ( nomadprovider "github.com/hashicorp/terraform/builtin/providers/nomad" ns1provider "github.com/hashicorp/terraform/builtin/providers/ns1" nullprovider "github.com/hashicorp/terraform/builtin/providers/null" + oneandoneprovider "github.com/hashicorp/terraform/builtin/providers/oneandone" opcprovider "github.com/hashicorp/terraform/builtin/providers/opc" openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack" opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie" @@ -126,6 +127,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{ "ns1": ns1provider.Provider, "null": nullprovider.Provider, "opc": opcprovider.Provider, + "oneandone": oneandoneprovider.Provider, "openstack": openstackprovider.Provider, "opsgenie": opsgenieprovider.Provider, "packet": packetprovider.Provider, diff --git a/config/resource_mode_string.go b/config/resource_mode_string.go index ea68b4fcd..930645fa8 100644 --- a/config/resource_mode_string.go +++ b/config/resource_mode_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT. +// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT package config diff --git a/helper/schema/getsource_string.go b/helper/schema/getsource_string.go index 3a9762939..790dbff91 100644 --- a/helper/schema/getsource_string.go +++ b/helper/schema/getsource_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT. +// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT package schema diff --git a/helper/schema/valuetype_string.go b/helper/schema/valuetype_string.go index 1610cec2d..08f008450 100644 --- a/helper/schema/valuetype_string.go +++ b/helper/schema/valuetype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT. +// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT package schema diff --git a/terraform/graphtype_string.go b/terraform/graphtype_string.go index e97b4855a..88ecad4f6 100644 --- a/terraform/graphtype_string.go +++ b/terraform/graphtype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT. +// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT package terraform diff --git a/terraform/instancetype_string.go b/terraform/instancetype_string.go index f69267cd5..f65414b34 100644 --- a/terraform/instancetype_string.go +++ b/terraform/instancetype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT. +// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT package terraform diff --git a/terraform/walkoperation_string.go b/terraform/walkoperation_string.go index cbd78dd93..8fb33d7b5 100644 --- a/terraform/walkoperation_string.go +++ b/terraform/walkoperation_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT. +// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT package terraform diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/LICENSE b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/LICENSE new file mode 100644 index 000000000..9fb7e22bc --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2016 1&1 Internet SE + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/README.md b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/README.md new file mode 100644 index 000000000..adb9cd19b --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/README.md @@ -0,0 +1,2573 @@ +# 1&1 Cloudserver Go SDK + +The 1&1 Go SDK is a Go library designed for interaction with the 1&1 cloud platform over the REST API. + +This guide contains instructions on getting started with the library and automating various management tasks available through the 1&1 Cloud Panel UI. + +## Table of Contents + +- [Overview](#overview) +- [Getting Started](#getting-started) + - [Installation](#installation) + - [Authentication](#authentication) +- [Operations](#operations) + - [Servers](#servers) + - [Images](#images) + - [Shared Storages](#shared-storages) + - [Firewall Policies](#firewall-policies) + - [Load Balancers](#load-balancers) + - [Public IPs](#public-ips) + - [Private Networks](#private-networks) + - [VPNs](#vpns) + - [Monitoring Center](#monitoring-center) + - [Monitoring Policies](#monitoring-policies) + - [Logs](#logs) + - [Users](#users) + - [Roles](#roles) + - [Usages](#usages) + - [Server Appliances](#server-appliances) + - [DVD ISO](#dvd-iso) + - [Ping](#ping) + - [Pricing](#pricing) + - [Data Centers](#data-centers) +- [Examples](#examples) +- [Index](#index) + +## Overview + +This SDK is a wrapper for the 1&1 REST API written in Go(lang). All operations against the API are performed over SSL and authenticated using your 1&1 token key. The Go library facilitates the access to the REST API either within an instance running on 1&1 platform or directly across the Internet from any HTTPS-enabled application. + +For more information on the 1&1 Cloud Server SDK for Go, visit the [Community Portal](https://www.1and1.com/cloud-community/). + +## Getting Started + +Before you begin you will need to have signed up for a 1&1 account. The credentials you create during sign-up will be used to authenticate against the API. + +Install the Go language tools. Find the install package and instructions on the official Go website. Make sure that you have set up the `GOPATH` environment variable properly, as indicated in the instructions. + +### Installation + +The official Go library is available from the 1&1 GitHub account found here. + +Use the following Go command to download oneandone-cloudserver-sdk-go to your configured GOPATH: + +`go get github.com/1and1/oneandone-cloudserver-sdk-go` + +Import the library in your Go code: + +`import "github.com/1and1/oneandone-cloudserver-sdk-go"` + +### Authentication + +Set the authentication token and create the API client: + +``` +token := oneandone.SetToken("82ee732b8d47e451be5c6ad5b7b56c81") +api := oneandone.New(token, oneandone.BaseUrl) +``` + +Refer to the [Examples](#examples) and [Operations](#operations) sections for additional information. + +## Operations + +### Servers + +**List all servers:** + +`servers, err := api.ListServers()` + +Alternatively, use the method with query parameters. + +`servers, err := api.ListServers(page, per_page, sort, query, fields)` + +To paginate the list of servers received in the response use `page` and `per_page` parameters. Set `per_page` to the number of servers that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of servers sorted in expected order pass a server property (e.g. `"name"`) in `sort` parameter. + +Use `query` parameter to search for a string in the response and return only the server instances that contain it. + +To retrieve a collection of servers containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,description,hardware.ram"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single server:** + +`server, err := api.GetServer(server_id)` + +**List fixed-size server templates:** + +`fiss, err := api.ListFixedInstanceSizes()` + +**Retrieve information about a fixed-size server template:** + +`fis, err := api.GetFixedInstanceSize(fis_id)` + +**Retrieve information about a server's hardware:** + +`hardware, err := api.GetServerHardware(server_id)` + +**List a server's HDDs:** + +`hdds, err := api.ListServerHdds(server_id)` + +**Retrieve a single server HDD:** + +`hdd, err := api.GetServerHdd(server_id, hdd_id)` + +**Retrieve information about a server's image:** + +`image, err := api.GetServerImage(server_id)` + +**List a server's IPs:** + +`ips, err := api.ListServerIps(server_id)` + +**Retrieve information about a single server IP:** + +`ip, err := api.GetServerIp(server_id, ip_id)` + +**Retrieve information about a server's firewall policy:** + +`firewall, err := api.GetServerIpFirewallPolicy(server_id, ip_id)` + +**List all load balancers assigned to a server IP:** + +`lbs, err := api.ListServerIpLoadBalancers(server_id, ip_id)` + +**Retrieve information about a server's status:** + +`status, err := api.GetServerStatus(server_id)` + +**Retrieve information about the DVD loaded into the virtual DVD unit of a server:** + +`dvd, err := api.GetServerDvd(server_id)` + +**List a server's private networks:** + +`pns, err := api.ListServerPrivateNetworks(server_id)` + +**Retrieve information about a server's private network:** + +`pn, err := api.GetServerPrivateNetwork(server_id, pn_id)` + +**Retrieve information about a server's snapshot:** + +`snapshot, err := api.GetServerSnapshot(server_id)` + +**Create a server:** + +``` +req := oneandone.ServerRequest { + Name: "Server Name", + Description: "Server description.", + ApplianceId: server_appliance_id, + PowerOn: true, + Hardware: oneandone.Hardware { + Vcores: 1, + CoresPerProcessor: 1, + Ram: 2, + Hdds: []oneandone.Hdd { + oneandone.Hdd { + Size: 100, + IsMain: true, + }, + }, + }, + } + +server_id, server, err := api.CreateServer(&req) +``` + +**Create a fixed-size server and return back the server's IP address and first password:** + +``` +req := oneandone.ServerRequest { + Name: server_name, + ApplianceId: server_appliance_id, + PowerOn: true_or_false, + Hardware: oneandone.Hardware { + FixedInsSizeId: fixed_instance_size_id, + }, + } + +ip_address, password, err := api.CreateServerEx(&req, timeout) +``` + +**Update a server:** + +`server, err := api.RenameServer(server_id, new_name, new_desc)` + +**Delete a server:** + +`server, err := api.DeleteServer(server_id, keep_ips)` + +Set `keep_ips` parameter to `true` for keeping server IPs after deleting a server. + +**Update a server's hardware:** + +``` +hardware := oneandone.Hardware { + Vcores: 2, + CoresPerProcessor: 1, + Ram: 2, + } + +server, err := api.UpdateServerHardware(server_id, &hardware) +``` + +**Add new hard disk(s) to a server:** + +``` +hdds := oneandone.ServerHdds { + Hdds: []oneandone.Hdd { + { + Size: 50, + IsMain: false, + }, + }, + } + +server, err := api.AddServerHdds(server_id, &hdds) +``` + +**Resize a server's hard disk:** + +`server, err := api.ResizeServerHdd(server_id, hdd_id, new_size)` + +**Remove a server's hard disk:** + +`server, err := api.DeleteServerHdd(server_id, hdd_id)` + +**Load a DVD into the virtual DVD unit of a server:** + +`server, err := api.LoadServerDvd(server_id, dvd_id)` + +**Unload a DVD from the virtual DVD unit of a server:** + +`server, err := api.EjectServerDvd(server_id)` + +**Reinstall a new image into a server:** + +`server, err := api.ReinstallServerImage(server_id, image_id, password, fp_id)` + +**Assign a new IP to a server:** + +`server, err := api.AssignServerIp(server_id, ip_type)` + +**Release an IP and optionally remove it from a server:** + +`server, err := api.DeleteServerIp(server_id, ip_id, keep_ip)` + +Set `keep_ip` to true for releasing the IP without removing it. + +**Assign a new firewall policy to a server's IP:** + +`server, err := api.AssignServerIpFirewallPolicy(server_id, ip_id, fp_id)` + +**Remove a firewall policy from a server's IP:** + +`server, err := api.UnassignServerIpFirewallPolicy(server_id, ip_id)` + +**Assign a new load balancer to a server's IP:** + +`server, err := api.AssignServerIpLoadBalancer(server_id, ip_id, lb_id)` + +**Remove a load balancer from a server's IP:** + +`server, err := api.UnassignServerIpLoadBalancer(server_id, ip_id, lb_id)` + +**Start a server:** + +`server, err := api.StartServer(server_id)` + +**Reboot a server:** + +`server, err := api.RebootServer(server_id, is_hardware)` + +Set `is_hardware` to true for HARDWARE method of rebooting. + +Set `is_hardware` to false for SOFTWARE method of rebooting. + +**Shutdown a server:** + +`server, err := api.ShutdownServer(server_id, is_hardware)` + +Set `is_hardware` to true for HARDWARE method of powering off. + +Set `is_hardware` to false for SOFTWARE method of powering off. + +**Assign a private network to a server:** + +`server, err := api.AssignServerPrivateNetwork(server_id, pn_id)` + +**Remove a server's private network:** + +`server, err := api.RemoveServerPrivateNetwork(server_id, pn_id)` + +**Create a new server's snapshot:** + +`server, err := api.CreateServerSnapshot(server_id)` + +**Restore a server's snapshot:** + +`server, err := api.RestoreServerSnapshot(server_id, snapshot_id)` + +**Remove a server's snapshot:** + +`server, err := api.DeleteServerSnapshot(server_id, snapshot_id);` + +**Clone a server:** + +`server, err := api.CloneServer(server_id, new_name)` + + +### Images + +**List all images:** + +`images, err = api.ListImages()` + +Alternatively, use the method with query parameters. + +`images, err = api.ListImages(page, per_page, sort, query, fields)` + +To paginate the list of images received in the response use `page` and `per_page` parameters. set `per_page` to the number of images that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of images sorted in expected order pass an image property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the elements that contain it. + +To retrieve a collection of images containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single image:** + +`image, err = api.GetImage(image_id)` + + +**Create an image:** + +``` +request := oneandone.ImageConfig { + Name: image_name, + Description: image_description, + ServerId: server_id, + Frequency: image_frequenct, + NumImages: number_of_images, + } + +image_id, image, err = api.CreateImage(&request) +``` +All fields except `Description` are required. `Frequency` may be set to `"ONCE"`, `"DAILY"` or `"WEEKLY"`. + +**Update an image:** + + +`image, err = api.UpdateImage(image_id, new_name, new_description, new_frequenct)` + +If any of the parameters `new_name`, `new_description` or `new_frequenct` is set to an empty string, it is ignored in the request. `Frequency` may be set to `"ONCE"`, `"DAILY"` or `"WEEKLY"`. + +**Delete an image:** + +`image, err = api.DeleteImage(image_id)` + +### Shared Storages + +`ss, err := api.ListSharedStorages()` + +Alternatively, use the method with query parameters. + +`ss, err := api.ListSharedStorages(page, per_page, sort, query, fields)` + +To paginate the list of shared storages received in the response use `page` and `per_page` parameters. Set `per_page` to the number of volumes that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of shared storages sorted in expected order pass a volume property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the volume instances that contain it. + +To retrieve a collection of shared storages containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,size,size_used"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a shared storage:** + +`ss, err := api.GetSharedStorage(ss_id)` + + +**Create a shared storage:** + +``` +request := oneandone.SharedStorageRequest { + Name: test_ss_name, + Description: test_ss_desc, + Size: oneandone.Int2Pointer(size), + } + +ss_id, ss, err := api.CreateSharedStorage(&request) + +``` +`Description` is optional parameter. + + +**Update a shared storage:** + +``` +request := oneandone.SharedStorageRequest { + Name: new_name, + Description: new_desc, + Size: oneandone.Int2Pointer(new_size), + } + +ss, err := api.UpdateSharedStorage(ss_id, &request) +``` +All request's parameters are optional. + + +**Remove a shared storage:** + +`ss, err := api.DeleteSharedStorage(ss_id)` + + +**List a shared storage servers:** + +`ss_servers, err := api.ListSharedStorageServers(ss_id)` + + +**Retrieve a shared storage server:** + +`ss_server, err := api.GetSharedStorageServer(ss_id, server_id)` + + +**Add servers to a shared storage:** + +``` +servers := []oneandone.SharedStorageServer { + { + Id: server_id, + Rights: permissions, + } , + } + +ss, err := api.AddSharedStorageServers(ss_id, servers) +``` +`Rights` may be set to `R` or `RW` string. + + +**Remove a server from a shared storage:** + +`ss, err := api.DeleteSharedStorageServer(ss_id, server_id)` + + +**Retrieve the credentials for accessing the shared storages:** + +`ss_credentials, err := api.GetSharedStorageCredentials()` + + +**Change the password for accessing the shared storages:** + +`ss_credentials, err := api.UpdateSharedStorageCredentials(new_password)` + + +### Firewall Policies + +**List firewall policies:** + +`firewalls, err := api.ListFirewallPolicies()` + +Alternatively, use the method with query parameters. + +`firewalls, err := api.ListFirewallPolicies(page, per_page, sort, query, fields)` + +To paginate the list of firewall policies received in the response use `page` and `per_page` parameters. Set `per_page` to the number of firewall policies that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of firewall policies sorted in expected order pass a firewall policy property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the firewall policy instances that contain it. + +To retrieve a collection of firewall policies containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single firewall policy:** + +`firewall, err := api.GetFirewallPolicy(fp_id)` + + +**Create a firewall policy:** + +``` +request := oneandone.FirewallPolicyRequest { + Name: fp_name, + Description: fp_desc, + Rules: []oneandone.FirewallPolicyRule { + { + Protocol: protocol, + PortFrom: oneandone.Int2Pointer(port_from), + PortTo: oneandone.Int2Pointer(port_to), + SourceIp: source_ip, + }, + }, + } + +firewall_id, firewall, err := api.CreateFirewallPolicy(&request) +``` +`SourceIp` and `Description` are optional parameters. + + +**Update a firewall policy:** + +`firewall, err := api.UpdateFirewallPolicy(fp_id, fp_new_name, fp_new_description)` + +Passing an empty string in `fp_new_name` or `fp_new_description` skips updating the firewall policy name or description respectively. + + +**Delete a firewall policy:** + +`firewall, err := api.DeleteFirewallPolicy(fp_id)` + + +**List servers/IPs attached to a firewall policy:** + +`server_ips, err := api.ListFirewallPolicyServerIps(fp_id)` + + +**Retrieve information about a server/IP assigned to a firewall policy:** + +`server_ip, err := api.GetFirewallPolicyServerIp(fp_id, ip_id)` + + +**Add servers/IPs to a firewall policy:** + +`firewall, err := api.AddFirewallPolicyServerIps(fp_id, ip_ids)` + +`ip_ids` is a slice of IP ID's. + + +**Remove a server/IP from a firewall policy:** + +`firewall, err := api.DeleteFirewallPolicyServerIp(fp_id, ip_id)` + + +**List rules of a firewall policy:** + +`fp_rules, err := api.ListFirewallPolicyRules(fp_id)` + + +**Retrieve information about a rule of a firewall policy:** + +`fp_rule, err := api.GetFirewallPolicyRule(fp_id, rule_id)` + + +**Adds new rules to a firewall policy:** + +``` +fp_rules := []oneandone.FirewallPolicyRule { + { + Protocol: protocol1, + PortFrom: oneandone.Int2Pointer(port_from1), + PortTo: oneandone.Int2Pointer(port_to1), + SourceIp: source_ip, + }, + { + Protocol: protocol2, + PortFrom: oneandone.Int2Pointer(port_from2), + PortTo: oneandone.Int2Pointer(port_to2), + }, + } + +firewall, err := api.AddFirewallPolicyRules(fp_id, fp_rules) +``` + +**Remove a rule from a firewall policy:** + +`firewall, err := api.DeleteFirewallPolicyRule(fp_id, rule_id)` + + +### Load Balancers + +**List load balancers:** + +`loadbalancers, err := api.ListLoadBalancers()` + +Alternatively, use the method with query parameters. + +`loadbalancers, err := api.ListLoadBalancers(page, per_page, sort, query, fields)` + +To paginate the list of load balancers received in the response use `page` and `per_page` parameters. Set `per_page` to the number of load balancers that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of load balancers sorted in expected order pass a load balancer property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the load balancer instances that contain it. + +To retrieve a collection of load balancers containing only the requested fields pass a list of comma separated properties (e.g. `"ip,name,method"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single load balancer:** + +`loadbalancer, err := api.GetLoadBalancer(lb_id)` + + +**Create a load balancer:** + +``` +request := oneandone.LoadBalancerRequest { + Name: lb_name, + Description: lb_description, + Method: lb_method, + Persistence: oneandone.Bool2Pointer(true_or_false), + PersistenceTime: oneandone.Int2Pointer(seconds1), + HealthCheckTest: protocol1, + HealthCheckInterval: oneandone.Int2Pointer(seconds2), + HealthCheckPath: health_check_path, + HealthCheckPathParser: health_check_path_parser, + Rules: []oneandone.LoadBalancerRule { + { + Protocol: protocol1, + PortBalancer: lb_port, + PortServer: server_port, + Source: source_ip, + }, + }, + } + +loadbalancer_id, loadbalancer, err := api.CreateLoadBalancer(&request) +``` +Optional parameters are `HealthCheckPath`, `HealthCheckPathParser`, `Source` and `Description`. Load balancer `Method` must be set to `"ROUND_ROBIN"` or `"LEAST_CONNECTIONS"`. + +**Update a load balancer:** +``` +request := oneandone.LoadBalancerRequest { + Name: new_name, + Description: new_description, + Persistence: oneandone.Bool2Pointer(true_or_false), + PersistenceTime: oneandone.Int2Pointer(new_seconds1), + HealthCheckTest: new_protocol, + HealthCheckInterval: oneandone.Int2Pointer(new_seconds2), + HealthCheckPath: new_path, + HealthCheckPathParser: new_parser, + Method: new_lb_method, + } + +loadbalancer, err := api.UpdateLoadBalancer(lb_id, &request) +``` +All updatable fields are optional. + + +**Delete a load balancer:** + +`loadbalancer, err := api.DeleteLoadBalancer(lb_id)` + + +**List servers/IPs attached to a load balancer:** + +`server_ips, err := api.ListLoadBalancerServerIps(lb_id)` + + +**Retrieve information about a server/IP assigned to a load balancer:** + +`server_ip, err := api.GetLoadBalancerServerIp(lb_id, ip_id)` + + +**Add servers/IPs to a load balancer:** + +`loadbalancer, err := api.AddLoadBalancerServerIps(lb_id, ip_ids)` + +`ip_ids` is a slice of IP ID's. + + +**Remove a server/IP from a load balancer:** + +`loadbalancer, err := api.DeleteLoadBalancerServerIp(lb_id, ip_id)` + + +**List rules of a load balancer:** + +`lb_rules, err := api.ListLoadBalancerRules(lb_id)` + + +**Retrieve information about a rule of a load balancer:** + +`lb_rule, err := api.GetLoadBalancerRule(lb_id, rule_id)` + + +**Adds new rules to a load balancer:** + +``` +lb_rules := []oneandone.LoadBalancerRule { + { + Protocol: protocol1, + PortBalancer: lb_port1, + PortServer: server_port1, + Source: source_ip, + }, + { + Protocol: protocol2, + PortBalancer: lb_port2, + PortServer: server_port2, + }, + } + +loadbalancer, err := api.AddLoadBalancerRules(lb_id, lb_rules) +``` + +**Remove a rule from a load balancer:** + +`loadbalancer, err := api.DeleteLoadBalancerRule(lb_id, rule_id)` + + +### Public IPs + +**Retrieve a list of your public IPs:** + +`public_ips, err := api.ListPublicIps()` + +Alternatively, use the method with query parameters. + +`public_ips, err := api.ListPublicIps(page, per_page, sort, query, fields)` + +To paginate the list of public IPs received in the response use `page` and `per_page` parameters. Set `per_page` to the number of public IPs that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of public IPs sorted in expected order pass a public IP property (e.g. `"ip"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the public IP instances that contain it. + +To retrieve a collection of public IPs containing only the requested fields pass a list of comma separated properties (e.g. `"id,ip,reverse_dns"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + + +**Retrieve a single public IP:** + +`public_ip, err := api.GetPublicIp(ip_id)` + + +**Create a public IP:** + +`ip_id, public_ip, err := api.CreatePublicIp(ip_type, reverse_dns)` + +Both parameters are optional and may be left blank. `ip_type` may be set to `"IPV4"` or `"IPV6"`. Presently, only IPV4 is supported. + +**Update the reverse DNS of a public IP:** + +`public_ip, err := api.UpdatePublicIp(ip_id, reverse_dns)` + +If an empty string is passed in `reverse_dns,` it removes previous reverse dns of the public IP. + +**Remove a public IP:** + +`public_ip, err := api.DeletePublicIp(ip_id)` + + +### Private Networks + +**List all private networks:** + +`private_nets, err := api.ListPrivateNetworks()` + +Alternatively, use the method with query parameters. + +`private_nets, err := api.ListPrivateNetworks(page, per_page, sort, query, fields)` + +To paginate the list of private networks received in the response use `page` and `per_page` parameters. Set `per_page` to the number of private networks that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of private networks sorted in expected order pass a private network property (e.g. `"-creation_date"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the private network instances that contain it. + +To retrieve a collection of private networks containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is blank, it is ignored in the request. + +**Retrieve information about a private network:** + +`private_net, err := api.GetPrivateNetwork(pn_id)` + +**Create a new private network:** + +``` +request := oneandone.PrivateNetworkRequest { + Name: pn_name, + Description: pn_description, + NetworkAddress: network_address, + SubnetMask: subnet_mask, + } + +pnet_id, private_net, err := api.CreatePrivateNetwork(&request) +``` +Private network `Name` is required parameter. + + +**Modify a private network:** + +``` +request := oneandone.PrivateNetworkRequest { + Name: new_pn_name, + Description: new_pn_description, + NetworkAddress: new_network_address, + SubnetMask: new_subnet_mask, + } + +private_net, err := api.UpdatePrivateNetwork(pn_id, &request) +``` +All parameters in the request are optional. + + +**Delete a private network:** + +`private_net, err := api.DeletePrivateNetwork(pn_id)` + + +**List all servers attached to a private network:** + +`servers, err = := api.ListPrivateNetworkServers(pn_id)` + + +**Retrieve a server attached to a private network:** + +`server, err = := api.GetPrivateNetworkServer(pn_id, server_id)` + + +**Attach servers to a private network:** + +`private_net, err := api.AttachPrivateNetworkServers(pn_id, server_ids)` + +`server_ids` is a slice of server ID's. + +*Note:* Servers cannot be attached to a private network if they currently have a snapshot. + + +**Remove a server from a private network:** + +`private_net, err := api.DetachPrivateNetworkServer(pn_id, server_id)` + +*Note:* The server cannot be removed from a private network if it currently has a snapshot or it is powered on. + + +### VPNs + +**List all VPNs:** + +`vpns, err := api.ListVPNs()` + +Alternatively, use the method with query parameters. + +`vpns, err := api.ListVPNs(page, per_page, sort, query, fields)` + +To paginate the list of VPNs received in the response use `page` and `per_page` parameters. Set ` per_page` to the number of VPNs that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of VPNs sorted in expected order pass a VPN property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the VPN instances that contain it. + +To retrieve a collection of VPNs containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve information about a VPN:** + +`vpn, err := api.GetVPN(vpn_id)` + +**Create a VPN:** + +`vpn, err := api.CreateVPN(vpn_name, vpn_description, datacenter_id)` + +**Modify a VPN:** + +`vpn, err := api.ModifyVPN(vpn_id, new_name, new_description)` + +**Delete a VPN:** + +`vpn, err := api.DeleteVPN(vpn_id)` + +**Retrieve a VPN's configuration file:** + +`base64_encoded_string, err := api.GetVPNConfigFile(vpn_id)` + + +### Monitoring Center + +**List all usages and alerts of monitoring servers:** + +`server_usages, err := api.ListMonitoringServersUsages()` + +Alternatively, use the method with query parameters. + +`server_usages, err := api.ListMonitoringServersUsages(page, per_page, sort, query, fields)` + +To paginate the list of server usages received in the response use `page` and `per_page` parameters. Set `per_page` to the number of server usages that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of server usages sorted in expected order pass a server usage property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the usage instances that contain it. + +To retrieve a collection of server usages containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,status.state"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is blank, it is ignored in the request. + +**Retrieve the usages and alerts for a monitoring server:** + +`server_usage, err := api.GetMonitoringServerUsage(server_id, period)` + +`period` may be set to `"LAST_HOUR"`, `"LAST_24H"`, `"LAST_7D"`, `"LAST_30D"`, `"LAST_365D"` or `"CUSTOM"`. If `period` is set to `"CUSTOM"`, the `start_date` and `end_date` parameters are required to be set in **RFC 3339** date/time format (e.g. `2015-13-12T00:01:00Z`). + +`server_usage, err := api.GetMonitoringServerUsage(server_id, period, start_date, end_date)` + +### Monitoring Policies + +**List all monitoring policies:** + +`mon_policies, err := api.ListMonitoringPolicies()` + +Alternatively, use the method with query parameters. + +`mon_policies, err := api.ListMonitoringPolicies(page, per_page, sort, query, fields)` + +To paginate the list of monitoring policies received in the response use `page` and `per_page` parameters. Set `per_page` to the number of monitoring policies that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of monitoring policies sorted in expected order pass a monitoring policy property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the monitoring policy instances that contain it. + +To retrieve a collection of monitoring policies containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single monitoring policy:** + +`mon_policy, err := api.GetMonitoringPolicy(mp_id)` + + +**Create a monitoring policy:** + +``` +request := oneandone.MonitoringPolicy { + Name: mp_name, + Description: mp_desc, + Email: mp_mail, + Agent: true_or_false, + Thresholds: &oneandone.MonitoringThreshold { + Cpu: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + }, + Ram: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + }, + Disk: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + }, + Transfer: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + }, + InternalPing: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: threshold_value, + Alert: true_or_false, + }, + }, + }, + Ports: []oneandone.MonitoringPort { + { + Protocol: protocol, + Port: port, + AlertIf: responding_or_not_responding, + EmailNotification: true_or_false, + }, + }, + Processes: []oneandone.MonitoringProcess { + { + Process: process_name, + AlertIf: running_or_not_running, + EmailNotification: true_or_false, + }, + }, + } + +mpolicy_id, mon_policy, err := api.CreateMonitoringPolicy(&request) +``` +All fields, except `Description`, are required. `AlertIf` property accepts values `"RESPONDING"`/`"NOT_RESPONDING"` for ports, and `"RUNNING"`/`"NOT_RUNNING"` for processes. + + +**Update a monitoring policy:** + +``` +request := oneandone.MonitoringPolicy { + Name: new_mp_name, + Description: new_mp_desc, + Email: new_mp_mail, + Thresholds: &oneandone.MonitoringThreshold { + Cpu: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + }, + Ram: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + }, + Disk: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + }, + Transfer: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + }, + InternalPing: &oneandone.MonitoringLevel { + Warning: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + Critical: &oneandone.MonitoringValue { + Value: new_threshold_value, + Alert: true_or_false, + }, + }, + }, + } + +mon_policy, err := api.UpdateMonitoringPolicy(mp_id, &request) +``` +All fields of the request are optional. When a threshold is specified in the request, the threshold fields are required. + +**Delete a monitoring policy:** + +`mon_policy, err := api.DeleteMonitoringPolicy(mp_id)` + + +**List all ports of a monitoring policy:** + +`mp_ports, err := api.ListMonitoringPolicyPorts(mp_id)` + + +**Retrieve information about a port of a monitoring policy:** + +`mp_port, err := api.GetMonitoringPolicyPort(mp_id, port_id)` + + +**Add new ports to a monitoring policy:** + +``` +mp_ports := []oneandone.MonitoringPort { + { + Protocol: protocol1, + Port: port1, + AlertIf: responding_or_not_responding, + EmailNotification: true_or_false, + }, + { + Protocol: protocol2, + Port: port2, + AlertIf: responding_or_not_responding, + EmailNotification: true_or_false, + }, + } + +mon_policy, err := api.AddMonitoringPolicyPorts(mp_id, mp_ports) +``` +Port properties are mandatory. + + +**Modify a port of a monitoring policy:** + +``` +mp_port := oneandone.MonitoringPort { + Protocol: protocol, + Port: port, + AlertIf: responding_or_not_responding, + EmailNotification: true_or_false, + } + +mon_policy, err := api.ModifyMonitoringPolicyPort(mp_id, port_id, &mp_port) +``` +*Note:* `Protocol` and `Port` cannot be changed. + + +**Remove a port from a monitoring policy:** + +`mon_policy, err := api.DeleteMonitoringPolicyPort(mp_id, port_id)` + + +**List the processes of a monitoring policy:** + +`mp_processes, err := api.ListMonitoringPolicyProcesses(mp_id)` + + +**Retrieve information about a process of a monitoring policy:** + +`mp_process, err := api.GetMonitoringPolicyProcess(mp_id, process_id)` + + +**Add new processes to a monitoring policy:** + +``` +processes := []oneandone.MonitoringProcess { + { + Process: process_name1, + AlertIf: running_or_not_running, + EmailNotification: true_or_false, + }, + { + Process: process_name2, + AlertIf: running_or_not_running, + EmailNotification: true_or_false, + }, + } + +mon_policy, err := api.AddMonitoringPolicyProcesses(mp_id, processes) +``` +All properties of the `MonitoringProcess` instance are required. + + +**Modify a process of a monitoring policy:** + +``` +process := oneandone.MonitoringProcess { + Process: process_name, + AlertIf: running_or_not_running, + EmailNotification: true_or_false, + } + +mon_policy, err := api.ModifyMonitoringPolicyProcess(mp_id, process_id, &process) +``` + +*Note:* Process name cannot be changed. + +**Remove a process from a monitoring policy:** + +`mon_policy, err := api.DeleteMonitoringPolicyProcess(mp_id, process_id)` + +**List all servers attached to a monitoring policy:** + +`mp_servers, err := api.ListMonitoringPolicyServers(mp_id)` + +**Retrieve information about a server attached to a monitoring policy:** + +`mp_server, err := api.GetMonitoringPolicyServer(mp_id, server_id)` + +**Attach servers to a monitoring policy:** + +`mon_policy, err := api.AttachMonitoringPolicyServers(mp_id, server_ids)` + +`server_ids` is a slice of server ID's. + +**Remove a server from a monitoring policy:** + +`mon_policy, err := api.RemoveMonitoringPolicyServer(mp_id, server_id)` + + +### Logs + +**List all logs:** + +`logs, err := api.ListLogs(period, nil, nil)` + +`period` can be set to `"LAST_HOUR"`, `"LAST_24H"`, `"LAST_7D"`, `"LAST_30D"`, `"LAST_365D"` or `"CUSTOM"`. If `period` is set to `"CUSTOM"`, the `start_date` and `end_date` parameters are required to be set in **RFC 3339** date/time format (e.g. `2015-13-12T00:01:00Z`). + +`logs, err := api.ListLogs(period, start_date, end_date)` + +Additional query parameters can be used. + +`logs, err := api.ListLogs(period, start_date, end_date, page, per_page, sort, query, fields)` + +To paginate the list of logs received in the response use `page` and `per_page` parameters. Set ` per_page` to the number of logs that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of logs sorted in expected order pass a logs property (e.g. `"action"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the logs instances that contain it. + +To retrieve a collection of logs containing only the requested fields pass a list of comma separated properties (e.g. `"id,action,type"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve a single log:** + +`log, err := api.GetLog(log_id)` + + +### Users + +**List all users:** + +`users, err := api.ListUsers()` + +Alternatively, use the method with query parameters. + +`users, err := api.ListUsers(page, per_page, sort, query, fields)` + +To paginate the list of users received in the response use `page` and `per_page` parameters. Set ` per_page` to the number of users that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of users sorted in expected order pass a user property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the user instances that contain it. + +To retrieve a collection of users containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date,email"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve information about a user:** + +`user, err := api.GetUser(user_id)` + +**Create a user:** + +``` +request := oneandone.UserRequest { + Name: username, + Description: user_description, + Password: password, + Email: user_email, + } + +user_id, user, err := api.CreateUser(&request) +``` + +`Name` and `Password` are required parameters. The password must contain at least 8 characters using uppercase letters, numbers and other special symbols. + +**Modify a user:** + +``` +request := oneandone.UserRequest { + Description: new_desc, + Email: new_mail, + Password: new_pass, + State: state, + } + +user, err := api.ModifyUser(user_id, &request) +``` + +All listed fields in the request are optional. `State` can be set to `"ACTIVE"` or `"DISABLED"`. + +**Delete a user:** + +`user, err := api.DeleteUser(user_id)` + +**Retrieve information about a user's API privileges:** + +`api_info, err := api.GetUserApi(user_id)` + +**Retrieve a user's API key:** + +`api_key, err := api.GetUserApiKey(user_id)` + +**List IP's from which API access is allowed for a user:** + +`allowed_ips, err := api.ListUserApiAllowedIps(user_id)` + +**Add new IP's to a user:** + +``` +user_ips := []string{ my_public_ip, "192.168.7.77", "10.81.12.101" } +user, err := api.AddUserApiAlowedIps(user_id, user_ips) +``` + +**Remove an IP and forbid API access from it:** + +`user, err := api.RemoveUserApiAllowedIp(user_id, ip)` + +**Modify a user's API privileges:** + +`user, err := api.ModifyUserApi(user_id, is_active)` + +**Renew a user's API key:** + +`user, err := api.RenewUserApiKey(user_id)` + +**Retrieve current user permissions:** + +`permissions, err := api.GetCurrentUserPermissions()` + + +### Roles + +**List all roles:** + +`roles, err := api.ListRoles()` + +Alternatively, use the method with query parameters. + +`roles, err := api.ListRoles(page, per_page, sort, query, fields)` + +To paginate the list of roles received in the response use `page` and `per_page` parameters. Set ` per_page` to the number of roles that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of roles sorted in expected order pass a role property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the role instances that contain it. + +To retrieve a collection of roles containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,creation_date"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + +**Retrieve information about a role:** + +`role, err := api.GetRole(role_id)` + +**Create a role:** + +`role, err := api.CreateRole(role_name)` + +**Clone a role:** + +`role, err := api.CloneRole(role_id, new_role_name)` + +**Modify a role:** + +`role, err := api.ModifyRole(role_id, new_name, new_description, new_state)` + +`ACTIVE` and `DISABLE` are valid values for the state. + +**Delete a role:** + +`role, err := api.DeleteRole(role_id)` + +**Retrieve information about a role's permissions:** + +`permissions, err := api.GetRolePermissions(role_id)` + +**Modify a role's permissions:** + +`role, err := api.ModifyRolePermissions(role_id, permissions)` + +**Assign users to a role:** + +`role, err := api.AssignRoleUsers(role_id, user_ids)` + +`user_ids` is a slice of user ID's. + +**List a role's users:** + +`users, err := api.ListRoleUsers(role_id)` + +**Retrieve information about a role's user:** + +`user, err := api.GetRoleUser(role_id, user_id)` + +**Remove a role's user:** + +`role, err := api.RemoveRoleUser(role_id, user_id)` + + +### Usages + +**List your usages:** + +`usages, err := api.ListUsages(period, nil, nil)` + +`period` can be set to `"LAST_HOUR"`, `"LAST_24H"`, `"LAST_7D"`, `"LAST_30D"`, `"LAST_365D"` or `"CUSTOM"`. If `period` is set to `"CUSTOM"`, the `start_date` and `end_date` parameters are required to be set in **RFC 3339** date/time format (e.g. `2015-13-12T00:01:00Z`). + +`usages, err := api.ListUsages(period, start_date, end_date)` + +Additional query parameters can be used. + +`usages, err := api.ListUsages(period, start_date, end_date, page, per_page, sort, query, fields)` + +To paginate the list of usages received in the response use `page` and `per_page` parameters. Set ` per_page` to the number of usages that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of usages sorted in expected order pass a usages property (e.g. `"name"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the usages instances that contain it. + +To retrieve a collection of usages containing only the requested fields pass a list of comma separated properties (e.g. `"id,name"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is set to an empty string, it is ignored in the request. + + +### Server Appliances + +**List all the appliances that you can use to create a server:** + +`server_appliances, err := api.ListServerAppliances()` + +Alternatively, use the method with query parameters. + +`server_appliances, err := api.ListServerAppliances(page, per_page, sort, query, fields)` + +To paginate the list of server appliances received in the response use `page` and `per_page` parameters. Set `per_page` to the number of server appliances that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of server appliances sorted in expected order pass a server appliance property (e.g. `"os"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the server appliance instances that contain it. + +To retrieve a collection of server appliances containing only the requested fields pass a list of comma separated properties (e.g. `"id,os,architecture"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is blank, it is ignored in the request. + +**Retrieve information about specific appliance:** + +`server_appliance, err := api.GetServerAppliance(appliance_id)` + + +### DVD ISO + +**List all operative systems and tools that you can load into your virtual DVD unit:** + +`dvd_isos, err := api.ListDvdIsos()` + +Alternatively, use the method with query parameters. + +`dvd_isos, err := api.ListDvdIsos(page, per_page, sort, query, fields)` + +To paginate the list of ISO DVDs received in the response use `page` and `per_page` parameters. Set `per_page` to the number of ISO DVDs that will be shown in each page. `page` indicates the current page. When set to an integer value that is less or equal to zero, the parameters are ignored by the framework. + +To receive the list of ISO DVDs sorted in expected order pass a ISO DVD property (e.g. `"type"`) in `sort` parameter. Prefix the sorting attribute with `-` sign for sorting in descending order. + +Use `query` parameter to search for a string in the response and return only the ISO DVD instances that contain it. + +To retrieve a collection of ISO DVDs containing only the requested fields pass a list of comma separated properties (e.g. `"id,name,type"`) in `fields` parameter. + +If any of the parameters `sort`, `query` or `fields` is blank, it is ignored in the request. + +**Retrieve a specific ISO image:** + +`dvd_iso, err := api.GetDvdIso(dvd_id)` + + +### Ping + +**Check if 1&1 REST API is running:** + +`response, err := api.Ping()` + +If the API is running, the response is a single-element slice `["PONG"]`. + +**Validate if 1&1 REST API is running and the authorization token is valid:** + +`response, err := api.PingAuth()` + +The response should be a single-element slice `["PONG"]` if the API is running and the token is valid. + + +### Pricing + +**Show prices for all available resources in the Cloud Panel:** + +`pricing, err := api.GetPricing()` + + +### Data Centers + +**List all 1&1 Cloud Server data centers:** + +`datacenters, err := api.ListDatacenters()` + +Here is another example of an alternative form of the list function that includes query parameters. + +`datacenters, err := api.ListDatacenters(0, 0, "country_code", "DE", "id,country_code")` + +**Retrieve a specific data center:** + +`datacenter, err := api.GetDatacenter(datacenter_id)` + + +## Examples + +```Go +package main + +import ( + "fmt" + "github.com/1and1/oneandone-cloudserver-sdk-go" + "time" +) + +func main() { + //Set an authentication token + token := oneandone.SetToken("82ee732b8d47e451be5c6ad5b7b56c81") + //Create an API client + api := oneandone.New(token, oneandone.BaseUrl) + + // List server appliances + saps, err := api.ListServerAppliances() + + var sa oneandone.ServerAppliance + for _, a := range saps { + if a.Type == "IMAGE" { + sa = a + } + } + + // Create a server + req := oneandone.ServerRequest{ + Name: "Example Server", + Description: "Example server description.", + ApplianceId: sa.Id, + PowerOn: true, + Hardware: oneandone.Hardware{ + Vcores: 1, + CoresPerProcessor: 1, + Ram: 2, + Hdds: []oneandone.Hdd { + oneandone.Hdd { + Size: sa.MinHddSize, + IsMain: true, + }, + }, + }, + } + + server_id, server, err := api.CreateServer(&req) + + if err == nil { + // Wait until server is created and powered on for at most 60 x 10 seconds + err = api.WaitForState(server, "POWERED_ON", 10, 60) + } + + // Get the server + server, err = api.GetServer(server_id) + + // Create a load balancer + lbr := oneandone.LoadBalancerRequest { + Name: "Load Balancer Example", + Description: "API created load balancer.", + Method: "ROUND_ROBIN", + Persistence: oneandone.Bool2Pointer(true), + PersistenceTime: oneandone.Int2Pointer(1200), + HealthCheckTest: "TCP", + HealthCheckInterval: oneandone.Int2Pointer(40), + Rules: []oneandone.LoadBalancerRule { + { + Protocol: "TCP", + PortBalancer: 80, + PortServer: 80, + Source: "0.0.0.0", + }, + }, + } + + var lb *oneandone.LoadBalancer + var lb_id string + + lb_id, lb, err = api.CreateLoadBalancer(&lbr) + if err != nil { + api.WaitForState(lb, "ACTIVE", 10, 30) + } + + // Get the load balancer + lb, err = api.GetLoadBalancer(lb.Id) + + // Assign the load balancer to server's IP + server, err = api.AssignServerIpLoadBalancer(server.Id, server.Ips[0].Id, lb_id) + + // Create a firewall policy + fpr := oneandone.FirewallPolicyRequest{ + Name: "Firewall Policy Example", + Description: "API created firewall policy.", + Rules: []oneandone.FirewallPolicyRule { + { + Protocol: "TCP", + PortFrom: oneandone.Int2Pointer(80), + PortTo: oneandone.Int2Pointer(80), + }, + }, + } + + var fp *oneandone.FirewallPolicy + + fp_id, fp, err = api.CreateFirewallPolicy(&fpr) + if err == nil { + api.WaitForState(fp, "ACTIVE", 10, 30) + } + + // Get the firewall policy + fp, err = api.GetFirewallPolicy(fp_id) + + // Add servers IPs to the firewall policy. + ips := []string{ server.Ips[0].Id } + + fp, err = api.AddFirewallPolicyServerIps(fp.Id, ips) + if err == nil { + api.WaitForState(fp, "ACTIVE", 10, 60) + } + + //Shutdown the server using 'SOFTWARE' method + server, err = api.ShutdownServer(server.Id, false) + if err != nil { + err = api.WaitForState(server, "POWERED_OFF", 5, 20) + } + + // Delete the load balancer + lb, err = api.DeleteLoadBalancer(lb.Id) + if err != nil { + err = api.WaitUntilDeleted(lb) + } + + // Delete the firewall policy + fp, err = api.DeleteFirewallPolicy(fp.Id) + if err != nil { + err = api.WaitUntilDeleted(fp) + } + + // List usages in last 24h + var usages *oneandone.Usages + usages, err = api.ListUsages("LAST_24H", nil, nil) + + fmt.Println(usages.Servers) + + // List usages in last 5 hours + n := time.Now() + ed := time.Date(n.Year(), n.Month(), n.Day(), n.Hour(), n.Minute(), n.Second(), 0, time.UTC) + sd := ed.Add(-(time.Hour * 5)) + + usages, err = api.ListUsages("CUSTOM", &sd, &ed) + + //Create a shared storage + ssr := oneandone.SharedStorageRequest { + Name: "Shared Storage Example", + Description: "API alocated 100 GB disk.", + Size: oneandone.Int2Pointer(100), + } + + var ss *oneandone.SharedStorage + var ss_id string + + ss_id, ss, err = api.CreateSharedStorage(&ssr) + if err != nil { + api.WaitForState(ss, "ACTIVE", 10, 30) + } + + // List shared storages on page 1, 5 results per page and sort by 'name' field. + // Include only 'name', 'size' and 'minimum_size_allowed' fields in the result. + var shs []oneandone.SharedStorage + shs, err = api.ListSharedStorages(1, 5, "name", "", "name,size,minimum_size_allowed") + + // List all shared storages that contain 'example' string + shs, err = api.ListSharedStorages(0, 0, "", "example", "") + + // Delete the shared storage + ss, err = api.DeleteSharedStorage(ss_id) + if err == nil { + err = api.WaitUntilDeleted(ss) + } + + // Delete the server + server, err = api.DeleteServer(server.Id, false) + if err == nil { + err = api.WaitUntilDeleted(server) + } +} + +``` +The next example illustrates how to create a `TYPO3` application server of a fixed size with an initial password and a firewall policy that has just been created. + +```Go +package main + +import "github.com/1and1/oneandone-cloudserver-sdk-go" + +func main() { + token := oneandone.SetToken("bde36026df9d548f699ea97e75a7e87f") + client := oneandone.New(token, oneandone.BaseUrl) + + // Create a new firewall policy + fpr := oneandone.FirewallPolicyRequest{ + Name: "HTTPS Traffic Policy", + Rules: []oneandone.FirewallPolicyRule{ + { + Protocol: "TCP", + PortFrom: oneandone.Int2Pointer(443), + PortTo: oneandone.Int2Pointer(443), + }, + }, + } + + _, fp, err := client.CreateFirewallPolicy(&fpr) + if fp != nil && err == nil { + client.WaitForState(fp, "ACTIVE", 5, 60) + + // Look for the TYPO3 application appliance + saps, _ := client.ListServerAppliances(0, 0, "", "typo3", "") + + var sa oneandone.ServerAppliance + for _, a := range saps { + if a.Type == "APPLICATION" { + sa = a + break + } + } + + var fixed_flavours []oneandone.FixedInstanceInfo + var fixed_size_id string + + fixed_flavours, err = client.ListFixedInstanceSizes() + for _, fl := range fixed_flavours { + //look for 'M' size + if fl.Name == "M" { + fixed_size_id = fl.Id + break + } + } + + req := oneandone.ServerRequest{ + Name: "TYPO3 Server", + ApplianceId: sa.Id, + PowerOn: true, + Password: "ucr_kXW8,.2SdMU", + Hardware: oneandone.Hardware{ + FixedInsSizeId: fixed_size_id, + }, + FirewallPolicyId: fp.Id, + } + _, server, _ := client.CreateServer(&req) + if server != nil { + client.WaitForState(server, "POWERED_ON", 10, 90) + } + } +} +``` + + +## Index + +```Go +func New(token string, url string) *API +``` + +```Go +func (api *API) AddFirewallPolicyRules(fp_id string, fp_rules []FirewallPolicyRule) (*FirewallPolicy, error) +``` + +```Go +func (api *API) AddFirewallPolicyServerIps(fp_id string, ip_ids []string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) AddLoadBalancerRules(lb_id string, lb_rules []LoadBalancerRule) (*LoadBalancer, error) +``` + +```Go +func (api *API) AddLoadBalancerServerIps(lb_id string, ip_ids []string) (*LoadBalancer, error) +``` + +```Go +func (api *API) AddMonitoringPolicyPorts(mp_id string, mp_ports []MonitoringPort) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) AddMonitoringPolicyProcesses(mp_id string, mp_procs []MonitoringProcess) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) AddServerHdds(server_id string, hdds *ServerHdds) (*Server, error) +``` + +```Go +func (api *API) AddSharedStorageServers(st_id string, servers []SharedStorageServer) (*SharedStorage, error) +``` + +```Go +func (api *API) AddUserApiAlowedIps(user_id string, ips []string) (*User, error) +``` + +```Go +func (api *API) AssignRoleUsers(role_id string, user_ids []string) (*Role, error) +``` + +```Go +func (api *API) AssignServerIp(server_id string, ip_type string) (*Server, error) +``` + +```Go +func (api *API) AssignServerIpFirewallPolicy(server_id string, ip_id string, fp_id string) (*Server, error) +``` + +```Go +func (api *API) AssignServerIpLoadBalancer(server_id string, ip_id string, lb_id string) (*Server, error) +``` + +```Go +func (api *API) AssignServerPrivateNetwork(server_id string, pn_id string) (*Server, error) +``` + +```Go +func (api *API) AttachMonitoringPolicyServers(mp_id string, sids []string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) AttachPrivateNetworkServers(pn_id string, sids []string) (*PrivateNetwork, error) +``` + +```Go +func (api *API) CloneRole(role_id string, name string) (*Role, error) +``` + +```Go +func (api *API) CloneServer(server_id string, new_name string, datacenter_id string) (*Server, error) +``` + +```Go +func (api *API) CreateFirewallPolicy(fp_data *FirewallPolicyRequest) (string, *FirewallPolicy, error) +``` + +```Go +func (api *API) CreateImage(request *ImageConfig) (string, *Image, error) +``` + +```Go +func (api *API) CreateLoadBalancer(request *LoadBalancerRequest) (string, *LoadBalancer, error) +``` + +```Go +func (api *API) CreateMonitoringPolicy(mp *MonitoringPolicy) (string, *MonitoringPolicy, error) +``` + +```Go +func (api *API) CreatePrivateNetwork(request *PrivateNetworkRequest) (string, *PrivateNetwork, error) +``` + +```Go +func (api *API) CreatePublicIp(ip_type string, reverse_dns string, datacenter_id string) (string, *PublicIp, error) +``` + +```Go +func (api *API) CreateRole(name string) (string, *Role, error) +``` + +```Go +func (api *API) CreateServer(request *ServerRequest) (string, *Server, error) +``` + +```Go +func (api *API) CreateServerEx(request *ServerRequest, timeout int) (string, string, error) +``` + +```Go +func (api *API) CreateServerSnapshot(server_id string) (*Server, error) +``` + +```Go +func (api *API) CreateSharedStorage(request *SharedStorageRequest) (string, *SharedStorage, error) +``` + +```Go +func (api *API) CreateUser(user *UserRequest) (string, *User, error) +``` + +```Go +func (api *API) CreateVPN(name string, description string, datacenter_id string) (string, *VPN, error) +``` + +```Go +func (api *API) DeleteFirewallPolicy(fp_id string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) DeleteFirewallPolicyRule(fp_id string, rule_id string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) DeleteFirewallPolicyServerIp(fp_id string, ip_id string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) DeleteImage(img_id string) (*Image, error) +``` + +```Go +func (api *API) DeleteLoadBalancer(lb_id string) (*LoadBalancer, error) +``` + +```Go +func (api *API) DeleteLoadBalancerRule(lb_id string, rule_id string) (*LoadBalancer, error) +``` + +```Go +func (api *API) DeleteLoadBalancerServerIp(lb_id string, ip_id string) (*LoadBalancer, error) +``` + +```Go +func (api *API) DeleteMonitoringPolicy(mp_id string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) DeleteMonitoringPolicyPort(mp_id string, port_id string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) DeleteMonitoringPolicyProcess(mp_id string, proc_id string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) DeletePrivateNetwork(pn_id string) (*PrivateNetwork, error) +``` + +```Go +func (api *API) DeletePublicIp(ip_id string) (*PublicIp, error) +``` + +```Go +func (api *API) DeleteRole(role_id string) (*Role, error) +``` + +```Go +func (api *API) DeleteServer(server_id string, keep_ips bool) (*Server, error) +``` + +```Go +func (api *API) DeleteServerHdd(server_id string, hdd_id string) (*Server, error) +``` + +```Go +func (api *API) DeleteServerIp(server_id string, ip_id string, keep_ip bool) (*Server, error) +``` + +```Go +func (api *API) DeleteServerSnapshot(server_id string, snapshot_id string) (*Server, error) +``` + +```Go +func (api *API) DeleteSharedStorage(ss_id string) (*SharedStorage, error) +``` + +```Go +func (api *API) DeleteSharedStorageServer(st_id string, ser_id string) (*SharedStorage, error) +``` + +```Go +func (api *API) DeleteUser(user_id string) (*User, error) +``` + +```Go +func (api *API) DeleteVPN(vpn_id string) (*VPN, error) +``` + +```Go +func (api *API) DetachPrivateNetworkServer(pn_id string, pns_id string) (*PrivateNetwork, error) +``` + +```Go +func (api *API) EjectServerDvd(server_id string) (*Server, error) +``` + +```Go +func (api *API) GetCurrentUserPermissions() (*Permissions, error) +``` + +```Go +func (api *API) GetDatacenter(dc_id string) (*Datacenter, error) +``` + +```Go +func (api *API) GetDvdIso(dvd_id string) (*DvdIso, error) +``` + +```Go +func (api *API) GetFirewallPolicy(fp_id string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) GetFirewallPolicyRule(fp_id string, rule_id string) (*FirewallPolicyRule, error) +``` + +```Go +func (api *API) GetFirewallPolicyServerIp(fp_id string, ip_id string) (*ServerIpInfo, error) +``` + +```Go +func (api *API) GetFixedInstanceSize(fis_id string) (*FixedInstanceInfo, error) +``` + +```Go +func (api *API) GetImage(img_id string) (*Image, error) +``` + +```Go +func (api *API) GetLoadBalancer(lb_id string) (*LoadBalancer, error) +``` + +```Go +func (api *API) GetLoadBalancerRule(lb_id string, rule_id string) (*LoadBalancerRule, error) +``` + +```Go +func (api *API) GetLoadBalancerServerIp(lb_id string, ip_id string) (*ServerIpInfo, error) +``` + +```Go +func (api *API) GetLog(log_id string) (*Log, error) +``` + +```Go +func (api *API) GetMonitoringPolicy(mp_id string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) GetMonitoringPolicyPort(mp_id string, port_id string) (*MonitoringPort, error) +``` + +```Go +func (api *API) GetMonitoringPolicyProcess(mp_id string, proc_id string) (*MonitoringProcess, error) +``` + +```Go +func (api *API) GetMonitoringPolicyServer(mp_id string, ser_id string) (*Identity, error) +``` + +```Go +func (api *API) GetMonitoringServerUsage(ser_id string, period string, dates ...time.Time) (*MonServerUsageDetails, error) +``` + +```Go +func (api *API) GetPricing() (*Pricing, error) +``` + +```Go +func (api *API) GetPrivateNetwork(pn_id string) (*PrivateNetwork, error) +``` + +```Go +func (api *API) GetPrivateNetworkServer(pn_id string, server_id string) (*Identity, error) +``` + +```Go +func (api *API) GetPublicIp(ip_id string) (*PublicIp, error) +``` + +```Go +func (api *API) GetRole(role_id string) (*Role, error) +``` + +```Go +func (api *API) GetRolePermissions(role_id string) (*Permissions, error) +``` + +```Go +func (api *API) GetRoleUser(role_id string, user_id string) (*Identity, error) +``` + +```Go +func (api *API) GetServer(server_id string) (*Server, error) +``` + +```Go +func (api *API) GetServerAppliance(sa_id string) (*ServerAppliance, error) +``` + +```Go +func (api *API) GetServerDvd(server_id string) (*Identity, error) +``` + +```Go +func (api *API) GetServerHardware(server_id string) (*Hardware, error) +``` + +```Go +func (api *API) GetServerHdd(server_id string, hdd_id string) (*Hdd, error) +``` + +```Go +func (api *API) GetServerImage(server_id string) (*Identity, error) +``` + +```Go +func (api *API) GetServerIp(server_id string, ip_id string) (*ServerIp, error) +``` + +```Go +func (api *API) GetServerIpFirewallPolicy(server_id string, ip_id string) (*Identity, error) +``` + +```Go +func (api *API) GetServerPrivateNetwork(server_id string, pn_id string) (*PrivateNetwork, error) +``` + +```Go +func (api *API) GetServerSnapshot(server_id string) (*ServerSnapshot, error) +``` + +```Go +func (api *API) GetServerStatus(server_id string) (*Status, error) +``` + +```Go +func (api *API) GetSharedStorage(ss_id string) (*SharedStorage, error) +``` + +```Go +func (api *API) GetSharedStorageCredentials() ([]SharedStorageAccess, error) +``` + +```Go +func (api *API) GetSharedStorageServer(st_id string, ser_id string) (*SharedStorageServer, error) +``` + +```Go +func (api *API) GetUser(user_id string) (*User, error) +``` + +```Go +func (api *API) GetUserApi(user_id string) (*UserApi, error) +``` + +```Go +func (api *API) GetUserApiKey(user_id string) (*UserApiKey, error) +``` + +```Go +func (api *API) GetVPN(vpn_id string) (*VPN, error) +``` + +```Go +func (api *API) GetVPNConfigFile(vpn_id string) (string, error) +``` + +```Go +func (api *API) ListDatacenters(args ...interface{}) ([]Datacenter, error) +``` + +```Go +func (api *API) ListDvdIsos(args ...interface{}) ([]DvdIso, error) +``` + +```Go +func (api *API) ListFirewallPolicies(args ...interface{}) ([]FirewallPolicy, error) +``` + +```Go +func (api *API) ListFirewallPolicyRules(fp_id string) ([]FirewallPolicyRule, error) +``` + +```Go +func (api *API) ListFirewallPolicyServerIps(fp_id string) ([]ServerIpInfo, error) +``` + +```Go +func (api *API) ListFixedInstanceSizes() ([]FixedInstanceInfo, error) +``` + +```Go +func (api *API) ListImages(args ...interface{}) ([]Image, error) +``` + +```Go +func (api *API) ListLoadBalancerRules(lb_id string) ([]LoadBalancerRule, error) +``` + +```Go +func (api *API) ListLoadBalancerServerIps(lb_id string) ([]ServerIpInfo, error) +``` + +```Go +func (api *API) ListLoadBalancers(args ...interface{}) ([]LoadBalancer, error) +``` + +```Go +func (api *API) ListLogs(period string, sd *time.Time, ed *time.Time, args ...interface{}) ([]Log, error) +``` + +```Go +func (api *API) ListMonitoringPolicies(args ...interface{}) ([]MonitoringPolicy, error) +``` + +```Go +func (api *API) ListMonitoringPolicyPorts(mp_id string) ([]MonitoringPort, error) +``` + +```Go +func (api *API) ListMonitoringPolicyProcesses(mp_id string) ([]MonitoringProcess, error) +``` + +```Go +func (api *API) ListMonitoringPolicyServers(mp_id string) ([]Identity, error) +``` + +```Go +func (api *API) ListMonitoringServersUsages(args ...interface{}) ([]MonServerUsageSummary, error) +``` + +```Go +func (api *API) ListPrivateNetworkServers(pn_id string) ([]Identity, error) +``` + +```Go +func (api *API) ListPrivateNetworks(args ...interface{}) ([]PrivateNetwork, error) +``` + +```Go +func (api *API) ListPublicIps(args ...interface{}) ([]PublicIp, error) +``` + +```Go +func (api *API) ListRoleUsers(role_id string) ([]Identity, error) +``` + +```Go +func (api *API) ListRoles(args ...interface{}) ([]Role, error) +``` + +```Go +func (api *API) ListServerAppliances(args ...interface{}) ([]ServerAppliance, error) +``` + +```Go +func (api *API) ListServerHdds(server_id string) ([]Hdd, error) +``` + +```Go +func (api *API) ListServerIpLoadBalancers(server_id string, ip_id string) ([]Identity, error) +``` + +```Go +func (api *API) ListServerIps(server_id string) ([]ServerIp, error) +``` + +```Go +func (api *API) ListServerPrivateNetworks(server_id string) ([]Identity, error) +``` + +```Go +func (api *API) ListServers(args ...interface{}) ([]Server, error) +``` + +```Go +func (api *API) ListSharedStorageServers(st_id string) ([]SharedStorageServer, error) +``` + +```Go +func (api *API) ListSharedStorages(args ...interface{}) ([]SharedStorage, error) +``` + +```Go +func (api *API) ListUsages(period string, sd *time.Time, ed *time.Time, args ...interface{}) (*Usages, error) +``` + +```Go +func (api *API) ListUserApiAllowedIps(user_id string) ([]string, error) +``` + +```Go +func (api *API) ListUsers(args ...interface{}) ([]User, error) +``` + +```Go +func (api *API) ListVPNs(args ...interface{}) ([]VPN, error) +``` + +```Go +func (api *API) LoadServerDvd(server_id string, dvd_id string) (*Server, error) +``` + +```Go +func (api *API) ModifyMonitoringPolicyPort(mp_id string, port_id string, mp_port *MonitoringPort) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) ModifyMonitoringPolicyProcess(mp_id string, proc_id string, mp_proc *MonitoringProcess) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) ModifyRole(role_id string, name string, description string, state string) (*Role, error) +``` + +```Go +func (api *API) ModifyRolePermissions(role_id string, perm *Permissions) (*Role, error) +``` + +```Go +func (api *API) ModifyUser(user_id string, user *UserRequest) (*User, error) +``` + +```Go +func (api *API) ModifyUserApi(user_id string, active bool) (*User, error) +``` + +```Go +func (api *API) ModifyVPN(vpn_id string, name string, description string) (*VPN, error) +``` + +```Go +func (api *API) Ping() ([]string, error) +``` + +```Go +func (api *API) PingAuth() ([]string, error) +``` + +```Go +func (api *API) RebootServer(server_id string, is_hardware bool) (*Server, error) +``` + +```Go +func (api *API) ReinstallServerImage(server_id string, image_id string, password string, fp_id string) (*Server, error) +``` + +```Go +func (api *API) RemoveMonitoringPolicyServer(mp_id string, ser_id string) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) RemoveRoleUser(role_id string, user_id string) (*Role, error) +``` + +```Go +func (api *API) RemoveServerPrivateNetwork(server_id string, pn_id string) (*Server, error) +``` + +```Go +func (api *API) RemoveUserApiAllowedIp(user_id string, ip string) (*User, error) +``` + +```Go +func (api *API) RenameServer(server_id string, new_name string, new_desc string) (*Server, error) +``` + +```Go +func (api *API) RenewUserApiKey(user_id string) (*User, error) +``` + +```Go +func (api *API) ResizeServerHdd(server_id string, hdd_id string, new_size int) (*Server, error) +``` + +```Go +func (api *API) RestoreServerSnapshot(server_id string, snapshot_id string) (*Server, error) +``` + +```Go +func (api *API) ShutdownServer(server_id string, is_hardware bool) (*Server, error) +``` + +```Go +func (api *API) StartServer(server_id string) (*Server, error) +``` + +```Go +func (api *API) UnassignServerIpFirewallPolicy(server_id string, ip_id string) (*Server, error) +``` + +```Go +func (api *API) UnassignServerIpLoadBalancer(server_id string, ip_id string, lb_id string) (*Server, error) +``` + +```Go +func (api *API) UpdateFirewallPolicy(fp_id string, fp_new_name string, fp_new_desc string) (*FirewallPolicy, error) +``` + +```Go +func (api *API) UpdateImage(img_id string, new_name string, new_desc string, new_freq string) (*Image, error) +``` + +```Go +func (api *API) UpdateLoadBalancer(lb_id string, request *LoadBalancerRequest) (*LoadBalancer, error) +``` + +```Go +func (api *API) UpdateMonitoringPolicy(mp_id string, mp *MonitoringPolicy) (*MonitoringPolicy, error) +``` + +```Go +func (api *API) UpdatePrivateNetwork(pn_id string, request *PrivateNetworkRequest) (*PrivateNetwork, error) +``` + +```Go +func (api *API) UpdatePublicIp(ip_id string, reverse_dns string) (*PublicIp, error) +``` + +```Go +func (api *API) UpdateServerHardware(server_id string, hardware *Hardware) (*Server, error) +``` + +```Go +func (api *API) UpdateSharedStorage(ss_id string, request *SharedStorageRequest) (*SharedStorage, error) +``` + +```Go +func (api *API) UpdateSharedStorageCredentials(new_pass string) ([]SharedStorageAccess, error) +``` + +```Go +func (api *API) WaitForState(in ApiInstance, state string, sec time.Duration, count int) error +``` + +```Go +func (api *API) WaitUntilDeleted(in ApiInstance) error +``` + +```Go +func (fp *FirewallPolicy) GetState() (string, error) +``` + +```Go +func (im *Image) GetState() (string, error) +``` + +```Go +func (lb *LoadBalancer) GetState() (string, error) +``` + +```Go +func (mp *MonitoringPolicy) GetState() (string, error) +``` + +```Go +func (pn *PrivateNetwork) GetState() (string, error) +``` + +```Go +func (ip *PublicIp) GetState() (string, error) +``` + +```Go +func (role *Role) GetState() (string, error) +``` + +```Go +func (s *Server) GetState() (string, error) +``` + +```Go +func (ss *SharedStorage) GetState() (string, error) +``` + +```Go +func (u *User) GetState() (string, error) +``` + +```Go +func (u *User) GetState() (string, error) +``` + +```Go +func (vpn *VPN) GetState() (string, error) +``` + +```Go +func Bool2Pointer(input bool) *bool +``` + +```Go +func Int2Pointer(input int) *int +``` + +```Go +func (bp *BackupPerm) SetAll(value bool) +``` + +```Go +func (fp *FirewallPerm) SetAll(value bool) +``` + +```Go +func (imp *ImagePerm) SetAll(value bool) +``` + +```Go +unc (inp *InvoicePerm) SetAll(value bool) +``` + +```Go +func (ipp *IPPerm) SetAll(value bool) +``` + +```Go +func (lbp *LoadBalancerPerm) SetAll(value bool) +``` + +```Go +func (lp *LogPerm) SetAll(value bool) +``` + +```Go +func (mcp *MonitorCenterPerm) SetAll(value bool) +``` + +```Go +func (mpp *MonitorPolicyPerm) SetAll(value bool) +``` + +```Go +func (p *Permissions) SetAll(v bool) +``` + +```Go +func (pnp *PrivateNetworkPerm) SetAll(value bool) +``` + +```Go +func (rp *RolePerm) SetAll(value bool) +``` + +```Go +func (sp *ServerPerm) SetAll(value bool) +``` + +```Go +func (ssp *SharedStoragePerm) SetAll(value bool) +``` + +```Go +func (up *UsagePerm) SetAll(value bool) +``` + +```Go +func (up *UserPerm) SetAll(value bool) +``` + +```Go +func (vpnp *VPNPerm) SetAll(value bool) +``` + +```Go +func SetBaseUrl(newbaseurl string) string +``` + +```Go +func SetToken(newtoken string) string +``` + diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/datacenters.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/datacenters.go new file mode 100644 index 000000000..cf193fb88 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/datacenters.go @@ -0,0 +1,36 @@ +package oneandone + +import "net/http" + +type Datacenter struct { + idField + CountryCode string `json:"country_code,omitempty"` + Location string `json:"location,omitempty"` +} + +// GET /datacenters +func (api *API) ListDatacenters(args ...interface{}) ([]Datacenter, error) { + url, err := processQueryParams(createUrl(api, datacenterPathSegment), args...) + if err != nil { + return nil, err + } + result := []Datacenter{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + + return result, nil +} + +// GET /datacenters/{datacenter_id} +func (api *API) GetDatacenter(dc_id string) (*Datacenter, error) { + result := new(Datacenter) + url := createUrl(api, datacenterPathSegment, dc_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/dvdisos.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/dvdisos.go new file mode 100644 index 000000000..ba54c3f7f --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/dvdisos.go @@ -0,0 +1,48 @@ +package oneandone + +import "net/http" + +// Struct to describe a ISO image that can be used to boot a server. +// +// Values of this type describe ISO images that can be inserted into the servers virtual DVD drive. +// +// +type DvdIso struct { + Identity + OsFamily string `json:"os_family,omitempty"` + Os string `json:"os,omitempty"` + OsVersion string `json:"os_version,omitempty"` + Type string `json:"type,omitempty"` + AvailableDatacenters []string `json:"available_datacenters,omitempty"` + Architecture interface{} `json:"os_architecture,omitempty"` + ApiPtr +} + +// GET /dvd_isos +func (api *API) ListDvdIsos(args ...interface{}) ([]DvdIso, error) { + url, err := processQueryParams(createUrl(api, dvdIsoPathSegment), args...) + if err != nil { + return nil, err + } + result := []DvdIso{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// GET /dvd_isos/{id} +func (api *API) GetDvdIso(dvd_id string) (*DvdIso, error) { + result := new(DvdIso) + url := createUrl(api, dvdIsoPathSegment, dvd_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/errors.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/errors.go new file mode 100644 index 000000000..08cc9c250 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/errors.go @@ -0,0 +1,27 @@ +package oneandone + +import ( + "fmt" +) + +type errorResponse struct { + Type string `json:"type"` + Message string `json:"message"` +} + +type apiError struct { + httpStatusCode int + message string +} + +func (e apiError) Error() string { + return fmt.Sprintf("%d - %s", e.httpStatusCode, e.message) +} + +func (e *apiError) HttpStatusCode() int { + return e.httpStatusCode +} + +func (e *apiError) Message() string { + return e.message +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/firewallpolicies.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/firewallpolicies.go new file mode 100644 index 000000000..3e89c9b17 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/firewallpolicies.go @@ -0,0 +1,208 @@ +package oneandone + +import ( + "net/http" +) + +type FirewallPolicy struct { + Identity + descField + DefaultPolicy uint8 `json:"default"` + CloudpanelId string `json:"cloudpanel_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + State string `json:"state,omitempty"` + Rules []FirewallPolicyRule `json:"rules,omitempty"` + ServerIps []ServerIpInfo `json:"server_ips,omitempty"` + ApiPtr +} + +type FirewallPolicyRule struct { + idField + Protocol string `json:"protocol,omitempty"` + PortFrom *int `json:"port_from,omitempty"` + PortTo *int `json:"port_to,omitempty"` + SourceIp string `json:"source,omitempty"` +} + +type FirewallPolicyRequest struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Rules []FirewallPolicyRule `json:"rules,omitempty"` +} + +// GET /firewall_policies +func (api *API) ListFirewallPolicies(args ...interface{}) ([]FirewallPolicy, error) { + url, err := processQueryParams(createUrl(api, firewallPolicyPathSegment), args...) + if err != nil { + return nil, err + } + result := []FirewallPolicy{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /firewall_policies +func (api *API) CreateFirewallPolicy(fp_data *FirewallPolicyRequest) (string, *FirewallPolicy, error) { + result := new(FirewallPolicy) + url := createUrl(api, firewallPolicyPathSegment) + err := api.Client.Post(url, &fp_data, &result, http.StatusAccepted) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /firewall_policies/{id} +func (api *API) GetFirewallPolicy(fp_id string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + url := createUrl(api, firewallPolicyPathSegment, fp_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil + +} + +// DELETE /firewall_policies/{id} +func (api *API) DeleteFirewallPolicy(fp_id string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + url := createUrl(api, firewallPolicyPathSegment, fp_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /firewall_policies/{id} +func (api *API) UpdateFirewallPolicy(fp_id string, fp_new_name string, fp_new_desc string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + data := FirewallPolicyRequest{ + Name: fp_new_name, + Description: fp_new_desc, + } + url := createUrl(api, firewallPolicyPathSegment, fp_id) + err := api.Client.Put(url, &data, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /firewall_policies/{id}/server_ips +func (api *API) ListFirewallPolicyServerIps(fp_id string) ([]ServerIpInfo, error) { + result := []ServerIpInfo{} + url := createUrl(api, firewallPolicyPathSegment, fp_id, "server_ips") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// GET /firewall_policies/{id}/server_ips/{id} +func (api *API) GetFirewallPolicyServerIp(fp_id string, ip_id string) (*ServerIpInfo, error) { + result := new(ServerIpInfo) + url := createUrl(api, firewallPolicyPathSegment, fp_id, "server_ips", ip_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /firewall_policies/{id}/server_ips +func (api *API) AddFirewallPolicyServerIps(fp_id string, ip_ids []string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + request := serverIps{ + ServerIps: ip_ids, + } + + url := createUrl(api, firewallPolicyPathSegment, fp_id, "server_ips") + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /firewall_policies/{id}/server_ips/{id} +func (api *API) DeleteFirewallPolicyServerIp(fp_id string, ip_id string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + url := createUrl(api, firewallPolicyPathSegment, fp_id, "server_ips", ip_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /firewall_policies/{id}/rules +func (api *API) ListFirewallPolicyRules(fp_id string) ([]FirewallPolicyRule, error) { + result := []FirewallPolicyRule{} + url := createUrl(api, firewallPolicyPathSegment, fp_id, "rules") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /firewall_policies/{id}/rules +func (api *API) AddFirewallPolicyRules(fp_id string, fp_rules []FirewallPolicyRule) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + data := struct { + Rules []FirewallPolicyRule `json:"rules"` + }{fp_rules} + url := createUrl(api, firewallPolicyPathSegment, fp_id, "rules") + err := api.Client.Post(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /firewall_policies/{id}/rules/{id} +func (api *API) GetFirewallPolicyRule(fp_id string, rule_id string) (*FirewallPolicyRule, error) { + result := new(FirewallPolicyRule) + url := createUrl(api, firewallPolicyPathSegment, fp_id, "rules", rule_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /firewall_policies/{id}/rules/{id} +func (api *API) DeleteFirewallPolicyRule(fp_id string, rule_id string) (*FirewallPolicy, error) { + result := new(FirewallPolicy) + url := createUrl(api, firewallPolicyPathSegment, fp_id, "rules", rule_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (fp *FirewallPolicy) GetState() (string, error) { + in, err := fp.api.GetFirewallPolicy(fp.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/images.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/images.go new file mode 100644 index 000000000..a3551cef7 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/images.go @@ -0,0 +1,110 @@ +package oneandone + +import ( + "net/http" +) + +type Image struct { + idField + ImageConfig + MinHddSize int `json:"min_hdd_size"` + Architecture *int `json:"os_architecture"` + CloudPanelId string `json:"cloudpanel_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + State string `json:"state,omitempty"` + OsImageType string `json:"os_image_type,omitempty"` + OsFamily string `json:"os_family,omitempty"` + Os string `json:"os,omitempty"` + OsVersion string `json:"os_version,omitempty"` + Type string `json:"type,omitempty"` + Licenses []License `json:"licenses,omitempty"` + Hdds []Hdd `json:"hdds,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + ApiPtr +} + +type ImageConfig struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Frequency string `json:"frequency,omitempty"` + ServerId string `json:"server_id,omitempty"` + NumImages int `json:"num_images"` +} + +// GET /images +func (api *API) ListImages(args ...interface{}) ([]Image, error) { + url, err := processQueryParams(createUrl(api, imagePathSegment), args...) + if err != nil { + return nil, err + } + result := []Image{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /images +func (api *API) CreateImage(request *ImageConfig) (string, *Image, error) { + res := new(Image) + url := createUrl(api, imagePathSegment) + err := api.Client.Post(url, &request, &res, http.StatusAccepted) + if err != nil { + return "", nil, err + } + res.api = api + return res.Id, res, nil +} + +// GET /images/{id} +func (api *API) GetImage(img_id string) (*Image, error) { + result := new(Image) + url := createUrl(api, imagePathSegment, img_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /images/{id} +func (api *API) DeleteImage(img_id string) (*Image, error) { + result := new(Image) + url := createUrl(api, imagePathSegment, img_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /images/{id} +func (api *API) UpdateImage(img_id string, new_name string, new_desc string, new_freq string) (*Image, error) { + result := new(Image) + req := struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Frequency string `json:"frequency,omitempty"` + }{Name: new_name, Description: new_desc, Frequency: new_freq} + url := createUrl(api, imagePathSegment, img_id) + err := api.Client.Put(url, &req, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (im *Image) GetState() (string, error) { + in, err := im.api.GetImage(im.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/loadbalancers.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/loadbalancers.go new file mode 100644 index 000000000..c965a25a8 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/loadbalancers.go @@ -0,0 +1,219 @@ +package oneandone + +import ( + "net/http" +) + +type LoadBalancer struct { + ApiPtr + idField + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + State string `json:"state,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Ip string `json:"ip,omitempty"` + HealthCheckTest string `json:"health_check_test,omitempty"` + HealthCheckInterval int `json:"health_check_interval"` + HealthCheckPath string `json:"health_check_path,omitempty"` + HealthCheckPathParser string `json:"health_check_path_parser,omitempty"` + Persistence bool `json:"persistence"` + PersistenceTime int `json:"persistence_time"` + Method string `json:"method,omitempty"` + Rules []LoadBalancerRule `json:"rules,omitempty"` + ServerIps []ServerIpInfo `json:"server_ips,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + CloudPanelId string `json:"cloudpanel_id,omitempty"` +} + +type LoadBalancerRule struct { + idField + Protocol string `json:"protocol,omitempty"` + PortBalancer uint16 `json:"port_balancer"` + PortServer uint16 `json:"port_server"` + Source string `json:"source,omitempty"` +} + +type LoadBalancerRequest struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + DatacenterId string `json:"datacenter_id,omitempty"` + HealthCheckTest string `json:"health_check_test,omitempty"` + HealthCheckInterval *int `json:"health_check_interval"` + HealthCheckPath string `json:"health_check_path,omitempty"` + HealthCheckPathParser string `json:"health_check_path_parser,omitempty"` + Persistence *bool `json:"persistence"` + PersistenceTime *int `json:"persistence_time"` + Method string `json:"method,omitempty"` + Rules []LoadBalancerRule `json:"rules,omitempty"` +} + +// GET /load_balancers +func (api *API) ListLoadBalancers(args ...interface{}) ([]LoadBalancer, error) { + url, err := processQueryParams(createUrl(api, loadBalancerPathSegment), args...) + if err != nil { + return nil, err + } + result := []LoadBalancer{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /load_balancers +func (api *API) CreateLoadBalancer(request *LoadBalancerRequest) (string, *LoadBalancer, error) { + url := createUrl(api, loadBalancerPathSegment) + result := new(LoadBalancer) + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /load_balancers/{id} +func (api *API) GetLoadBalancer(lb_id string) (*LoadBalancer, error) { + url := createUrl(api, loadBalancerPathSegment, lb_id) + result := new(LoadBalancer) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /load_balancers/{id} +func (api *API) DeleteLoadBalancer(lb_id string) (*LoadBalancer, error) { + url := createUrl(api, loadBalancerPathSegment, lb_id) + result := new(LoadBalancer) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /load_balancers/{id} +func (api *API) UpdateLoadBalancer(lb_id string, request *LoadBalancerRequest) (*LoadBalancer, error) { + url := createUrl(api, loadBalancerPathSegment, lb_id) + result := new(LoadBalancer) + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /load_balancers/{id}/server_ips +func (api *API) ListLoadBalancerServerIps(lb_id string) ([]ServerIpInfo, error) { + result := []ServerIpInfo{} + url := createUrl(api, loadBalancerPathSegment, lb_id, "server_ips") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// GET /load_balancers/{id}/server_ips/{id} +func (api *API) GetLoadBalancerServerIp(lb_id string, ip_id string) (*ServerIpInfo, error) { + result := new(ServerIpInfo) + url := createUrl(api, loadBalancerPathSegment, lb_id, "server_ips", ip_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /load_balancers/{id}/server_ips +func (api *API) AddLoadBalancerServerIps(lb_id string, ip_ids []string) (*LoadBalancer, error) { + result := new(LoadBalancer) + request := serverIps{ + ServerIps: ip_ids, + } + url := createUrl(api, loadBalancerPathSegment, lb_id, "server_ips") + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /load_balancers/{id}/server_ips/{id} +func (api *API) DeleteLoadBalancerServerIp(lb_id string, ip_id string) (*LoadBalancer, error) { + result := new(LoadBalancer) + url := createUrl(api, loadBalancerPathSegment, lb_id, "server_ips", ip_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /load_balancers/{load_balancer_id}/rules +func (api *API) ListLoadBalancerRules(lb_id string) ([]LoadBalancerRule, error) { + result := []LoadBalancerRule{} + url := createUrl(api, loadBalancerPathSegment, lb_id, "rules") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /load_balancers/{load_balancer_id}/rules +func (api *API) AddLoadBalancerRules(lb_id string, lb_rules []LoadBalancerRule) (*LoadBalancer, error) { + result := new(LoadBalancer) + data := struct { + Rules []LoadBalancerRule `json:"rules"` + }{lb_rules} + url := createUrl(api, loadBalancerPathSegment, lb_id, "rules") + err := api.Client.Post(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /load_balancers/{load_balancer_id}/rules/{rule_id} +func (api *API) GetLoadBalancerRule(lb_id string, rule_id string) (*LoadBalancerRule, error) { + result := new(LoadBalancerRule) + url := createUrl(api, loadBalancerPathSegment, lb_id, "rules", rule_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /load_balancers/{load_balancer_id}/rules/{rule_id} +func (api *API) DeleteLoadBalancerRule(lb_id string, rule_id string) (*LoadBalancer, error) { + result := new(LoadBalancer) + url := createUrl(api, loadBalancerPathSegment, lb_id, "rules", rule_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (lb *LoadBalancer) GetState() (string, error) { + in, err := lb.api.GetLoadBalancer(lb.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/logs.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/logs.go new file mode 100644 index 000000000..b16ef31d5 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/logs.go @@ -0,0 +1,50 @@ +package oneandone + +import ( + "net/http" + "time" +) + +type Log struct { + ApiPtr + idField + typeField + CloudPanelId string `json:"cloudpanel_id,omitempty"` + SiteId string `json:"site_id,omitempty"` + StartDate string `json:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty"` + Action string `json:"action,omitempty"` + Duration int `json:"duration"` + Status *Status `json:"Status,omitempty"` + Resource *Identity `json:"resource,omitempty"` + User *Identity `json:"user,omitempty"` +} + +// GET /logs +func (api *API) ListLogs(period string, sd *time.Time, ed *time.Time, args ...interface{}) ([]Log, error) { + result := []Log{} + url, err := processQueryParamsExt(createUrl(api, logPathSegment), period, sd, ed, args...) + if err != nil { + return nil, err + } + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// GET /logs/{id} +func (api *API) GetLog(log_id string) (*Log, error) { + result := new(Log) + url := createUrl(api, logPathSegment, log_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringcenter.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringcenter.go new file mode 100644 index 000000000..86e899889 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringcenter.go @@ -0,0 +1,158 @@ +package oneandone + +import ( + "errors" + "net/http" + "time" +) + +type MonServerUsageSummary struct { + Identity + Agent *monitoringAgent `json:"agent,omitempty"` + Alerts *monitoringAlerts `json:"alerts,omitempty"` + Status *monitoringStatus `json:"status,omitempty"` + ApiPtr +} + +type MonServerUsageDetails struct { + Identity + Status *statusState `json:"status,omitempty"` + Agent *monitoringAgent `json:"agent,omitempty"` + Alerts *monitoringAlerts `json:"alerts,omitempty"` + CpuStatus *utilizationStatus `json:"cpu,omitempty"` + DiskStatus *utilizationStatus `json:"disk,omitempty"` + RamStatus *utilizationStatus `json:"ram,omitempty"` + PingStatus *pingStatus `json:"internal_ping,omitempty"` + TransferStatus *transferStatus `json:"transfer,omitempty"` + ApiPtr +} + +type monitoringStatus struct { + State string `json:"state,omitempty"` + Cpu *statusState `json:"cpu,omitempty"` + Disk *statusState `json:"disk,omitempty"` + InternalPing *statusState `json:"internal_ping,omitempty"` + Ram *statusState `json:"ram,omitempty"` + Transfer *statusState `json:"transfer,omitempty"` +} + +type utilizationStatus struct { + CriticalThreshold int `json:"critical,omitempty"` + WarningThreshold int `json:"warning,omitempty"` + Status string `json:"status,omitempty"` + Data []usageData `json:"data,omitempty"` + Unit *usageUnit `json:"unit,omitempty"` +} + +type pingStatus struct { + CriticalThreshold int `json:"critical,omitempty"` + WarningThreshold int `json:"warning,omitempty"` + Status string `json:"status,omitempty"` + Data []pingData `json:"data,omitempty"` + Unit *pingUnit `json:"unit,omitempty"` +} + +type transferStatus struct { + CriticalThreshold int `json:"critical,omitempty"` + WarningThreshold int `json:"warning,omitempty"` + Status string `json:"status,omitempty"` + Data []transferData `json:"data,omitempty"` + Unit *transferUnit `json:"unit,omitempty"` +} + +type monitoringAgent struct { + AgentInstalled bool `json:"agent_installed"` + MissingAgentAlert bool `json:"missing_agent_alert"` + MonitoringNeedsAgent bool `json:"monitoring_needs_agent"` +} + +type monitoringAlerts struct { + Ports *monitoringAlertInfo `json:"ports,omitempty"` + Process *monitoringAlertInfo `json:"process,omitempty"` + Resources *monitoringAlertInfo `json:"resources,omitempty"` +} + +type monitoringAlertInfo struct { + Ok int `json:"ok"` + Warning int `json:"warning"` + Critical int `json:"critical"` +} + +type usageData struct { + Date string `json:"date,omitempty"` + UsedPercent float32 `json:"used_percent"` +} + +type usageUnit struct { + UsedPercent string `json:"used_percent,omitempty"` +} + +type pingUnit struct { + PackagesLost string `json:"pl,omitempty"` + AccessTime string `json:"rta,omitempty"` +} + +type pingData struct { + Date string `json:"date,omitempty"` + PackagesLost int `json:"pl"` + AccessTime float32 `json:"rta"` +} + +type transferUnit struct { + Downstream string `json:"downstream,omitempty"` + Upstream string `json:"upstream,omitempty"` +} + +type transferData struct { + Date string `json:"date,omitempty"` + Downstream int `json:"downstream"` + Upstream int `json:"upstream"` +} + +// GET /monitoring_center +func (api *API) ListMonitoringServersUsages(args ...interface{}) ([]MonServerUsageSummary, error) { + url, err := processQueryParams(createUrl(api, monitorCenterPathSegment), args...) + if err != nil { + return nil, err + } + result := []MonServerUsageSummary{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// GET /monitoring_center/{server_id} +func (api *API) GetMonitoringServerUsage(ser_id string, period string, dates ...time.Time) (*MonServerUsageDetails, error) { + if period == "" { + return nil, errors.New("Time period must be provided.") + } + + params := make(map[string]interface{}, len(dates)+1) + params["period"] = period + + if len(dates) == 2 { + if dates[0].After(dates[1]) { + return nil, errors.New("Start date cannot be after end date.") + } + + params["start_date"] = dates[0].Format(time.RFC3339) + params["end_date"] = dates[1].Format(time.RFC3339) + + } else if len(dates) > 0 { + return nil, errors.New("Start and end dates must be provided.") + } + url := createUrl(api, monitorCenterPathSegment, ser_id) + url = appendQueryParams(url, params) + result := new(MonServerUsageDetails) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringpolicies.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringpolicies.go new file mode 100644 index 000000000..4272461b6 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/monitoringpolicies.go @@ -0,0 +1,305 @@ +package oneandone + +import ( + "net/http" +) + +type MonitoringPolicy struct { + ApiPtr + idField + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + State string `json:"state,omitempty"` + Default *int `json:"default,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Email string `json:"email,omitempty"` + Agent bool `json:"agent"` + Servers []Identity `json:"servers,omitempty"` + Thresholds *MonitoringThreshold `json:"thresholds,omitempty"` + Ports []MonitoringPort `json:"ports,omitempty"` + Processes []MonitoringProcess `json:"processes,omitempty"` + CloudPanelId string `json:"cloudpanel_id,omitempty"` +} + +type MonitoringThreshold struct { + Cpu *MonitoringLevel `json:"cpu,omitempty"` + Ram *MonitoringLevel `json:"ram,omitempty"` + Disk *MonitoringLevel `json:"disk,omitempty"` + Transfer *MonitoringLevel `json:"transfer,omitempty"` + InternalPing *MonitoringLevel `json:"internal_ping,omitempty"` +} + +type MonitoringLevel struct { + Warning *MonitoringValue `json:"warning,omitempty"` + Critical *MonitoringValue `json:"critical,omitempty"` +} + +type MonitoringValue struct { + Value int `json:"value"` + Alert bool `json:"alert"` +} + +type MonitoringPort struct { + idField + Protocol string `json:"protocol,omitempty"` + Port int `json:"port"` + AlertIf string `json:"alert_if,omitempty"` + EmailNotification bool `json:"email_notification"` +} + +type MonitoringProcess struct { + idField + Process string `json:"process,omitempty"` + AlertIf string `json:"alert_if,omitempty"` + EmailNotification bool `json:"email_notification"` +} + +// GET /monitoring_policies +func (api *API) ListMonitoringPolicies(args ...interface{}) ([]MonitoringPolicy, error) { + url, err := processQueryParams(createUrl(api, monitorPolicyPathSegment), args...) + if err != nil { + return nil, err + } + result := []MonitoringPolicy{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /monitoring_policies +func (api *API) CreateMonitoringPolicy(mp *MonitoringPolicy) (string, *MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment) + err := api.Client.Post(url, &mp, &result, http.StatusCreated) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /monitoring_policies/{id} +func (api *API) GetMonitoringPolicy(mp_id string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment, mp_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /monitoring_policies/{id} +func (api *API) DeleteMonitoringPolicy(mp_id string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment, mp_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /monitoring_policies/{id} +func (api *API) UpdateMonitoringPolicy(mp_id string, mp *MonitoringPolicy) (*MonitoringPolicy, error) { + url := createUrl(api, monitorPolicyPathSegment, mp_id) + result := new(MonitoringPolicy) + err := api.Client.Put(url, &mp, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/ports +func (api *API) ListMonitoringPolicyPorts(mp_id string) ([]MonitoringPort, error) { + result := []MonitoringPort{} + url := createUrl(api, monitorPolicyPathSegment, mp_id, "ports") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /monitoring_policies/{id}/ports +func (api *API) AddMonitoringPolicyPorts(mp_id string, mp_ports []MonitoringPort) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + data := struct { + Ports []MonitoringPort `json:"ports"` + }{mp_ports} + url := createUrl(api, monitorPolicyPathSegment, mp_id, "ports") + err := api.Client.Post(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/ports/{id} +func (api *API) GetMonitoringPolicyPort(mp_id string, port_id string) (*MonitoringPort, error) { + result := new(MonitoringPort) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "ports", port_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /monitoring_policies/{id}/ports/{id} +func (api *API) DeleteMonitoringPolicyPort(mp_id string, port_id string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "ports", port_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /monitoring_policies/{id}/ports/{id} +func (api *API) ModifyMonitoringPolicyPort(mp_id string, port_id string, mp_port *MonitoringPort) (*MonitoringPolicy, error) { + url := createUrl(api, monitorPolicyPathSegment, mp_id, "ports", port_id) + result := new(MonitoringPolicy) + req := struct { + Ports *MonitoringPort `json:"ports"` + }{mp_port} + err := api.Client.Put(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/processes +func (api *API) ListMonitoringPolicyProcesses(mp_id string) ([]MonitoringProcess, error) { + result := []MonitoringProcess{} + url := createUrl(api, monitorPolicyPathSegment, mp_id, "processes") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /monitoring_policies/{id}/processes +func (api *API) AddMonitoringPolicyProcesses(mp_id string, mp_procs []MonitoringProcess) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + request := struct { + Processes []MonitoringProcess `json:"processes"` + }{mp_procs} + url := createUrl(api, monitorPolicyPathSegment, mp_id, "processes") + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/processes/{id} +func (api *API) GetMonitoringPolicyProcess(mp_id string, proc_id string) (*MonitoringProcess, error) { + result := new(MonitoringProcess) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "processes", proc_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /monitoring_policies/{id}/processes/{id} +func (api *API) DeleteMonitoringPolicyProcess(mp_id string, proc_id string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "processes", proc_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /monitoring_policies/{id}/processes/{id} +func (api *API) ModifyMonitoringPolicyProcess(mp_id string, proc_id string, mp_proc *MonitoringProcess) (*MonitoringPolicy, error) { + url := createUrl(api, monitorPolicyPathSegment, mp_id, "processes", proc_id) + result := new(MonitoringPolicy) + req := struct { + Processes *MonitoringProcess `json:"processes"` + }{mp_proc} + err := api.Client.Put(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/servers +func (api *API) ListMonitoringPolicyServers(mp_id string) ([]Identity, error) { + result := []Identity{} + url := createUrl(api, monitorPolicyPathSegment, mp_id, "servers") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /monitoring_policies/{id}/servers +func (api *API) AttachMonitoringPolicyServers(mp_id string, sids []string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + request := servers{ + Servers: sids, + } + url := createUrl(api, monitorPolicyPathSegment, mp_id, "servers") + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /monitoring_policies/{id}/servers/{id} +func (api *API) GetMonitoringPolicyServer(mp_id string, ser_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "servers", ser_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /monitoring_policies/{id}/servers/{id} +func (api *API) RemoveMonitoringPolicyServer(mp_id string, ser_id string) (*MonitoringPolicy, error) { + result := new(MonitoringPolicy) + url := createUrl(api, monitorPolicyPathSegment, mp_id, "servers", ser_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (mp *MonitoringPolicy) GetState() (string, error) { + in, err := mp.api.GetMonitoringPolicy(mp.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/oneandone.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/oneandone.go new file mode 100644 index 000000000..e007fcb22 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/oneandone.go @@ -0,0 +1,163 @@ +package oneandone + +import ( + "errors" + "net/http" + "reflect" + "time" +) + +// Struct to hold the required information for accessing the API. +// +// Instances of this type contain the URL of the endpoint to access the API as well as the API access token to be used. +// They offer also all methods that allow to access the various objects that are returned by top level resources of +// the API. +type API struct { + Endpoint string + Client *restClient +} + +type ApiPtr struct { + api *API +} + +type idField struct { + Id string `json:"id,omitempty"` +} + +type typeField struct { + Type string `json:"type,omitempty"` +} + +type nameField struct { + Name string `json:"name,omitempty"` +} + +type descField struct { + Description string `json:"description,omitempty"` +} + +type countField struct { + Count int `json:"count,omitempty"` +} + +type serverIps struct { + ServerIps []string `json:"server_ips"` +} + +type servers struct { + Servers []string `json:"servers"` +} + +type ApiInstance interface { + GetState() (string, error) +} + +const ( + datacenterPathSegment = "datacenters" + dvdIsoPathSegment = "dvd_isos" + firewallPolicyPathSegment = "firewall_policies" + imagePathSegment = "images" + loadBalancerPathSegment = "load_balancers" + logPathSegment = "logs" + monitorCenterPathSegment = "monitoring_center" + monitorPolicyPathSegment = "monitoring_policies" + pingPathSegment = "ping" + pingAuthPathSegment = "ping_auth" + pricingPathSegment = "pricing" + privateNetworkPathSegment = "private_networks" + publicIpPathSegment = "public_ips" + rolePathSegment = "roles" + serverPathSegment = "servers" + serverAppliancePathSegment = "server_appliances" + sharedStoragePathSegment = "shared_storages" + usagePathSegment = "usages" + userPathSegment = "users" + vpnPathSegment = "vpns" +) + +// Struct to hold the status of an API object. +// +// Values of this type are used to represent the status of API objects like servers, firewall policies and the like. +// +// The value of the "State" field can represent fixed states like "ACTIVE" or "POWERED_ON" but also transitional +// states like "POWERING_ON" or "CONFIGURING". +// +// For fixed states the "Percent" field is empty where as for transitional states it contains the progress of the +// transition in percent. +type Status struct { + State string `json:"state"` + Percent int `json:"percent"` +} + +type statusState struct { + State string `json:"state,omitempty"` +} + +type Identity struct { + idField + nameField +} + +type License struct { + nameField +} + +// Creates a new API instance. +// +// Explanations about given token and url information can be found online under the following url TODO add url! +func New(token string, url string) *API { + api := new(API) + api.Endpoint = url + api.Client = newRestClient(token) + return api +} + +// Converts a given integer value into a pointer of the same type. +func Int2Pointer(input int) *int { + result := new(int) + *result = input + return result +} + +// Converts a given boolean value into a pointer of the same type. +func Bool2Pointer(input bool) *bool { + result := new(bool) + *result = input + return result +} + +// Performs busy-waiting for types that implement ApiInstance interface. +func (api *API) WaitForState(in ApiInstance, state string, sec time.Duration, count int) error { + if in != nil { + for i := 0; i < count; i++ { + s, err := in.GetState() + if err != nil { + return err + } + if s == state { + return nil + } + time.Sleep(sec * time.Second) + } + return errors.New(reflect.ValueOf(in).Type().String() + " operation timeout.") + } + return nil +} + +// Waits until instance is deleted for types that implement ApiInstance interface. +func (api *API) WaitUntilDeleted(in ApiInstance) error { + var err error + for in != nil { + _, err = in.GetState() + if err != nil { + if apiError, ok := err.(apiError); ok && apiError.httpStatusCode == http.StatusNotFound { + return nil + } else { + return err + } + } + time.Sleep(5 * time.Second) + } + return nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/ping.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/ping.go new file mode 100644 index 000000000..255608885 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/ping.go @@ -0,0 +1,29 @@ +package oneandone + +import "net/http" + +// GET /ping +// Returns "PONG" if API is running +func (api *API) Ping() ([]string, error) { + url := createUrl(api, pingPathSegment) + result := []string{} + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + + return result, nil +} + +// GET /ping_auth +// Returns "PONG" if the API is running and the authentication token is valid +func (api *API) PingAuth() ([]string, error) { + url := createUrl(api, pingAuthPathSegment) + result := []string{} + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/pricing.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/pricing.go new file mode 100644 index 000000000..90eb2abd9 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/pricing.go @@ -0,0 +1,40 @@ +package oneandone + +import "net/http" + +type Pricing struct { + Currency string `json:"currency,omitempty"` + Plan *pricingPlan `json:"pricing_plans,omitempty"` +} + +type pricingPlan struct { + Image *pricingItem `json:"image,omitempty"` + PublicIPs []pricingItem `json:"public_ips,omitempty"` + Servers *serverPricing `json:"servers,omitempty"` + SharedStorage *pricingItem `json:"shared_storage,omitempty"` + SoftwareLicenses []pricingItem `json:"software_licences,omitempty"` +} + +type serverPricing struct { + FixedServers []pricingItem `json:"fixed_servers,omitempty"` + FlexServers []pricingItem `json:"flexible_server,omitempty"` +} + +type pricingItem struct { + Name string `json:"name,omitempty"` + GrossPrice string `json:"price_gross,omitempty"` + NetPrice string `json:"price_net,omitempty"` + Unit string `json:"unit,omitempty"` +} + +// GET /pricing +func (api *API) GetPricing() (*Pricing, error) { + result := new(Pricing) + url := createUrl(api, pricingPathSegment) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/privatenetworks.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/privatenetworks.go new file mode 100644 index 000000000..667494e04 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/privatenetworks.go @@ -0,0 +1,149 @@ +package oneandone + +import ( + "net/http" +) + +type PrivateNetwork struct { + Identity + descField + CloudpanelId string `json:"cloudpanel_id,omitempty"` + NetworkAddress string `json:"network_address,omitempty"` + SubnetMask string `json:"subnet_mask,omitempty"` + State string `json:"state,omitempty"` + SiteId string `json:"site_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Servers []Identity `json:"servers,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + ApiPtr +} + +type PrivateNetworkRequest struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + DatacenterId string `json:"datacenter_id,omitempty"` + NetworkAddress string `json:"network_address,omitempty"` + SubnetMask string `json:"subnet_mask,omitempty"` +} + +// GET /private_networks +func (api *API) ListPrivateNetworks(args ...interface{}) ([]PrivateNetwork, error) { + url, err := processQueryParams(createUrl(api, privateNetworkPathSegment), args...) + if err != nil { + return nil, err + } + result := []PrivateNetwork{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /private_networks +func (api *API) CreatePrivateNetwork(request *PrivateNetworkRequest) (string, *PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, privateNetworkPathSegment) + err := api.Client.Post(url, &request, &result, http.StatusAccepted) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /private_networks/{id} +func (api *API) GetPrivateNetwork(pn_id string) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, privateNetworkPathSegment, pn_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /private_networks/{id} +func (api *API) UpdatePrivateNetwork(pn_id string, request *PrivateNetworkRequest) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, privateNetworkPathSegment, pn_id) + err := api.Client.Put(url, &request, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /private_networks/{id} +func (api *API) DeletePrivateNetwork(pn_id string) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, privateNetworkPathSegment, pn_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /private_networks/{id}/servers +func (api *API) ListPrivateNetworkServers(pn_id string) ([]Identity, error) { + result := []Identity{} + url := createUrl(api, privateNetworkPathSegment, pn_id, "servers") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /private_networks/{id}/servers +func (api *API) AttachPrivateNetworkServers(pn_id string, sids []string) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + req := servers{ + Servers: sids, + } + url := createUrl(api, privateNetworkPathSegment, pn_id, "servers") + err := api.Client.Post(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /private_networks/{id}/servers/{id} +func (api *API) GetPrivateNetworkServer(pn_id string, server_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, privateNetworkPathSegment, pn_id, "servers", server_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /private_networks/{id}/servers/{id} +func (api *API) DetachPrivateNetworkServer(pn_id string, pns_id string) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, privateNetworkPathSegment, pn_id, "servers", pns_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (pn *PrivateNetwork) GetState() (string, error) { + in, err := pn.api.GetPrivateNetwork(pn.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/publicips.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/publicips.go new file mode 100644 index 000000000..b0b6bd6ed --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/publicips.go @@ -0,0 +1,108 @@ +package oneandone + +import "net/http" + +type PublicIp struct { + idField + typeField + IpAddress string `json:"ip,omitempty"` + AssignedTo *assignedTo `json:"assigned_to,omitempty"` + ReverseDns string `json:"reverse_dns,omitempty"` + IsDhcp *bool `json:"is_dhcp,omitempty"` + State string `json:"state,omitempty"` + SiteId string `json:"site_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + ApiPtr +} + +type assignedTo struct { + Identity + typeField +} + +const ( + IpTypeV4 = "IPV4" + IpTypeV6 = "IPV6" +) + +// GET /public_ips +func (api *API) ListPublicIps(args ...interface{}) ([]PublicIp, error) { + url, err := processQueryParams(createUrl(api, publicIpPathSegment), args...) + if err != nil { + return nil, err + } + result := []PublicIp{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /public_ips +func (api *API) CreatePublicIp(ip_type string, reverse_dns string, datacenter_id string) (string, *PublicIp, error) { + res := new(PublicIp) + url := createUrl(api, publicIpPathSegment) + req := struct { + DatacenterId string `json:"datacenter_id,omitempty"` + ReverseDns string `json:"reverse_dns,omitempty"` + Type string `json:"type,omitempty"` + }{DatacenterId: datacenter_id, ReverseDns: reverse_dns, Type: ip_type} + err := api.Client.Post(url, &req, &res, http.StatusCreated) + if err != nil { + return "", nil, err + } + res.api = api + return res.Id, res, nil +} + +// GET /public_ips/{id} +func (api *API) GetPublicIp(ip_id string) (*PublicIp, error) { + result := new(PublicIp) + url := createUrl(api, publicIpPathSegment, ip_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /public_ips/{id} +func (api *API) DeletePublicIp(ip_id string) (*PublicIp, error) { + result := new(PublicIp) + url := createUrl(api, publicIpPathSegment, ip_id) + err := api.Client.Delete(url, nil, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /public_ips/{id} +func (api *API) UpdatePublicIp(ip_id string, reverse_dns string) (*PublicIp, error) { + result := new(PublicIp) + url := createUrl(api, publicIpPathSegment, ip_id) + req := struct { + ReverseDns string `json:"reverse_dns,omitempty"` + }{reverse_dns} + err := api.Client.Put(url, &req, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (ip *PublicIp) GetState() (string, error) { + in, err := ip.api.GetPublicIp(ip.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/restclient.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/restclient.go new file mode 100644 index 000000000..b200a1089 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/restclient.go @@ -0,0 +1,213 @@ +package oneandone + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + p_url "net/url" + "time" +) + +type restClient struct { + token string +} + +func newRestClient(token string) *restClient { + restClient := new(restClient) + restClient.token = token + return restClient +} + +func (c *restClient) Get(url string, result interface{}, expectedStatus int) error { + return c.doRequest(url, "GET", nil, result, expectedStatus) +} + +func (c *restClient) Delete(url string, requestBody interface{}, result interface{}, expectedStatus int) error { + return c.doRequest(url, "DELETE", requestBody, result, expectedStatus) +} + +func (c *restClient) Post(url string, requestBody interface{}, result interface{}, expectedStatus int) error { + return c.doRequest(url, "POST", requestBody, result, expectedStatus) +} + +func (c *restClient) Put(url string, requestBody interface{}, result interface{}, expectedStatus int) error { + return c.doRequest(url, "PUT", requestBody, result, expectedStatus) +} + +func (c *restClient) doRequest(url string, method string, requestBody interface{}, result interface{}, expectedStatus int) error { + var bodyData io.Reader + if requestBody != nil { + data, _ := json.Marshal(requestBody) + bodyData = bytes.NewBuffer(data) + } + + request, err := http.NewRequest(method, url, bodyData) + if err != nil { + return err + } + + request.Header.Add("X-Token", c.token) + request.Header.Add("Content-Type", "application/json") + client := http.Client{} + response, err := client.Do(request) + if err = isError(response, expectedStatus, err); err != nil { + return err + } + + defer response.Body.Close() + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + return c.unmarshal(body, result) +} + +func (c *restClient) unmarshal(data []byte, result interface{}) error { + err := json.Unmarshal(data, result) + if err != nil { + // handle the case when the result is an empty array instead of an object + switch err.(type) { + case *json.UnmarshalTypeError: + var ra []interface{} + e := json.Unmarshal(data, &ra) + if e != nil { + return e + } else if len(ra) > 0 { + return err + } + return nil + default: + return err + } + } + + return nil +} + +func isError(response *http.Response, expectedStatus int, err error) error { + if err != nil { + return err + } + if response != nil { + if response.StatusCode == expectedStatus { + // we got a response with the expected HTTP status code, hence no error + return nil + } + body, _ := ioutil.ReadAll(response.Body) + // extract the API's error message to be returned later + er_resp := new(errorResponse) + err = json.Unmarshal(body, er_resp) + if err != nil { + return err + } + + return apiError{response.StatusCode, fmt.Sprintf("Type: %s; Message: %s", er_resp.Type, er_resp.Message)} + } + return errors.New("Generic error - no response from the REST API service.") +} + +func createUrl(api *API, sections ...interface{}) string { + url := api.Endpoint + for _, section := range sections { + url += "/" + fmt.Sprint(section) + } + return url +} + +func makeParameterMap(args ...interface{}) (map[string]interface{}, error) { + qps := make(map[string]interface{}, len(args)) + var is_true bool + var page, per_page int + var sort, query, fields string + + for i, p := range args { + switch i { + case 0: + page, is_true = p.(int) + if !is_true { + return nil, errors.New("1st parameter must be a page number (integer).") + } else if page > 0 { + qps["page"] = page + } + case 1: + per_page, is_true = p.(int) + if !is_true { + return nil, errors.New("2nd parameter must be a per_page number (integer).") + } else if per_page > 0 { + qps["per_page"] = per_page + } + case 2: + sort, is_true = p.(string) + if !is_true { + return nil, errors.New("3rd parameter must be a sorting property string (e.g. 'name' or '-name').") + } else if sort != "" { + qps["sort"] = sort + } + case 3: + query, is_true = p.(string) + if !is_true { + return nil, errors.New("4th parameter must be a query string to look for the response.") + } else if query != "" { + qps["q"] = query + } + case 4: + fields, is_true = p.(string) + if !is_true { + return nil, errors.New("5th parameter must be fields properties string (e.g. 'id,name').") + } else if fields != "" { + qps["fields"] = fields + } + default: + return nil, errors.New("Wrong number of parameters.") + } + } + return qps, nil +} + +func processQueryParams(url string, args ...interface{}) (string, error) { + if len(args) > 0 { + params, err := makeParameterMap(args...) + if err != nil { + return "", err + } + url = appendQueryParams(url, params) + } + return url, nil +} + +func processQueryParamsExt(url string, period string, sd *time.Time, ed *time.Time, args ...interface{}) (string, error) { + var qm map[string]interface{} + var err error + if len(args) > 0 { + qm, err = makeParameterMap(args...) + if err != nil { + return "", err + } + } else { + qm = make(map[string]interface{}, 3) + } + qm["period"] = period + if sd != nil && ed != nil { + if sd.After(*ed) { + return "", errors.New("Start date cannot be after end date.") + } + qm["start_date"] = sd.Format(time.RFC3339) + qm["end_date"] = ed.Format(time.RFC3339) + } + url = appendQueryParams(url, qm) + return url, nil +} + +func appendQueryParams(url string, params map[string]interface{}) string { + queryUrl, _ := p_url.Parse(url) + parameters := p_url.Values{} + for key, value := range params { + parameters.Add(key, fmt.Sprintf("%v", value)) + } + queryUrl.RawQuery = parameters.Encode() + return queryUrl.String() +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/roles.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/roles.go new file mode 100644 index 000000000..e8aa44fee --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/roles.go @@ -0,0 +1,595 @@ +package oneandone + +import "net/http" + +type Role struct { + Identity + descField + CreationDate string `json:"creation_date,omitempty"` + State string `json:"state,omitempty"` + Default *int `json:"default,omitempty"` + Permissions *Permissions `json:"permissions,omitempty"` + Users []Identity `json:"users,omitempty"` + ApiPtr +} + +type Permissions struct { + Backups *BackupPerm `json:"backups,omitempty"` + Firewalls *FirewallPerm `json:"firewall_policies,omitempty"` + Images *ImagePerm `json:"images,omitempty"` + Invoice *InvoicePerm `json:"interactive_invoices,omitempty"` + IPs *IPPerm `json:"public_ips,omitempty"` + LoadBalancers *LoadBalancerPerm `json:"load_balancers,omitempty"` + Logs *LogPerm `json:"logs,omitempty"` + MonitorCenter *MonitorCenterPerm `json:"monitoring_center,omitempty"` + MonitorPolicies *MonitorPolicyPerm `json:"monitoring_policies,omitempty"` + PrivateNetworks *PrivateNetworkPerm `json:"private_networks,omitempty"` + Roles *RolePerm `json:"roles,omitempty"` + Servers *ServerPerm `json:"servers,omitempty"` + SharedStorage *SharedStoragePerm `json:"shared_storages,omitempty"` + Usages *UsagePerm `json:"usages,omitempty"` + Users *UserPerm `json:"users,omitempty"` + VPNs *VPNPerm `json:"vpn,omitempty"` +} + +type BackupPerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + Show bool `json:"show"` +} + +type FirewallPerm struct { + Clone bool `json:"clone"` + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageAttachedServerIPs bool `json:"manage_attached_server_ips"` + ManageRules bool `json:"manage_rules"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type ImagePerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + DisableAutoCreate bool `json:"disable_automatic_creation"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type InvoicePerm struct { + Show bool `json:"show"` +} + +type IPPerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + Release bool `json:"release"` + SetReverseDNS bool `json:"set_reverse_dns"` + Show bool `json:"show"` +} + +type LoadBalancerPerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageAttachedServerIPs bool `json:"manage_attached_server_ips"` + ManageRules bool `json:"manage_rules"` + Modify bool `json:"modify"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type LogPerm struct { + Show bool `json:"show"` +} + +type MonitorCenterPerm struct { + Show bool `json:"show"` +} + +type MonitorPolicyPerm struct { + Clone bool `json:"clone"` + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageAttachedServers bool `json:"manage_attached_servers"` + ManagePorts bool `json:"manage_ports"` + ManageProcesses bool `json:"manage_processes"` + ModifyResources bool `json:"modify_resources"` + SetDescription bool `json:"set_description"` + SetEmail bool `json:"set_email"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type PrivateNetworkPerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageAttachedServers bool `json:"manage_attached_servers"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + SetNetworkInfo bool `json:"set_network_info"` + Show bool `json:"show"` +} + +type RolePerm struct { + Clone bool `json:"clone"` + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageUsers bool `json:"manage_users"` + Modify bool `json:"modify"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type ServerPerm struct { + AccessKVMConsole bool `json:"access_kvm_console"` + AssignIP bool `json:"assign_ip"` + Clone bool `json:"clone"` + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageDVD bool `json:"manage_dvd"` + ManageSnapshot bool `json:"manage_snapshot"` + Reinstall bool `json:"reinstall"` + Resize bool `json:"resize"` + Restart bool `json:"restart"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` + Shutdown bool `json:"shutdown"` + Start bool `json:"start"` +} + +type SharedStoragePerm struct { + Access bool `json:"access"` + Create bool `json:"create"` + Delete bool `json:"delete"` + ManageAttachedServers bool `json:"manage_attached_servers"` + Resize bool `json:"resize"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +type UsagePerm struct { + Show bool `json:"show"` +} + +type UserPerm struct { + ChangeRole bool `json:"change_role"` + Create bool `json:"create"` + Delete bool `json:"delete"` + Disable bool `json:"disable"` + Enable bool `json:"enable"` + ManageAPI bool `json:"manage_api"` + SetDescription bool `json:"set_description"` + SetEmail bool `json:"set_email"` + SetPassword bool `json:"set_password"` + Show bool `json:"show"` +} + +type VPNPerm struct { + Create bool `json:"create"` + Delete bool `json:"delete"` + DownloadFile bool `json:"download_file"` + SetDescription bool `json:"set_description"` + SetName bool `json:"set_name"` + Show bool `json:"show"` +} + +// GET /roles +func (api *API) ListRoles(args ...interface{}) ([]Role, error) { + url, err := processQueryParams(createUrl(api, rolePathSegment), args...) + if err != nil { + return nil, err + } + result := []Role{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for _, role := range result { + role.api = api + } + return result, nil +} + +// POST /roles +func (api *API) CreateRole(name string) (string, *Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment) + req := struct { + Name string `json:"name"` + }{name} + err := api.Client.Post(url, &req, &result, http.StatusCreated) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /roles/{role_id} +func (api *API) GetRole(role_id string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /roles/{role_id} +func (api *API) ModifyRole(role_id string, name string, description string, state string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id) + req := struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + State string `json:"state,omitempty"` + }{Name: name, Description: description, State: state} + err := api.Client.Put(url, &req, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /roles/{role_id} +func (api *API) DeleteRole(role_id string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id) + err := api.Client.Delete(url, nil, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /roles/{role_id}/permissions +func (api *API) GetRolePermissions(role_id string) (*Permissions, error) { + result := new(Permissions) + url := createUrl(api, rolePathSegment, role_id, "permissions") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /roles/{role_id}/permissions +func (api *API) ModifyRolePermissions(role_id string, perm *Permissions) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id, "permissions") + err := api.Client.Put(url, &perm, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /roles/{role_id}/users +func (api *API) ListRoleUsers(role_id string) ([]Identity, error) { + result := []Identity{} + url := createUrl(api, rolePathSegment, role_id, "users") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /roles/{role_id}/users +func (api *API) AssignRoleUsers(role_id string, user_ids []string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id, "users") + req := struct { + Users []string `json:"users"` + }{user_ids} + err := api.Client.Post(url, &req, &result, http.StatusCreated) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /roles/{role_id}/users/{user_id} +func (api *API) GetRoleUser(role_id string, user_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, rolePathSegment, role_id, "users", user_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /roles/{role_id}/users/{user_id} +func (api *API) RemoveRoleUser(role_id string, user_id string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id, "users", user_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// POST /roles/{role_id}/clone +func (api *API) CloneRole(role_id string, name string) (*Role, error) { + result := new(Role) + url := createUrl(api, rolePathSegment, role_id, "clone") + req := struct { + Name string `json:"name"` + }{name} + err := api.Client.Post(url, &req, &result, http.StatusCreated) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +func (role *Role) GetState() (string, error) { + in, err := role.api.GetRole(role.Id) + if in == nil { + return "", err + } + return in.State, err +} + +// Sets all backups' permissions +func (bp *BackupPerm) SetAll(value bool) { + bp.Create = value + bp.Delete = value + bp.Show = value +} + +// Sets all firewall policies' permissions +func (fp *FirewallPerm) SetAll(value bool) { + fp.Clone = value + fp.Create = value + fp.Delete = value + fp.ManageAttachedServerIPs = value + fp.ManageRules = value + fp.SetDescription = value + fp.SetName = value + fp.Show = value +} + +// Sets all images' permissions +func (imp *ImagePerm) SetAll(value bool) { + imp.Create = value + imp.Delete = value + imp.DisableAutoCreate = value + imp.SetDescription = value + imp.SetName = value + imp.Show = value +} + +// Sets all invoice's permissions +func (inp *InvoicePerm) SetAll(value bool) { + inp.Show = value +} + +// Sets all IPs' permissions +func (ipp *IPPerm) SetAll(value bool) { + ipp.Create = value + ipp.Delete = value + ipp.Release = value + ipp.SetReverseDNS = value + ipp.Show = value +} + +// Sets all load balancers' permissions +func (lbp *LoadBalancerPerm) SetAll(value bool) { + lbp.Create = value + lbp.Delete = value + lbp.ManageAttachedServerIPs = value + lbp.ManageRules = value + lbp.Modify = value + lbp.SetDescription = value + lbp.SetName = value + lbp.Show = value +} + +// Sets all logs' permissions +func (lp *LogPerm) SetAll(value bool) { + lp.Show = value +} + +// Sets all monitoring center's permissions +func (mcp *MonitorCenterPerm) SetAll(value bool) { + mcp.Show = value +} + +// Sets all monitoring policies' permissions +func (mpp *MonitorPolicyPerm) SetAll(value bool) { + mpp.Clone = value + mpp.Create = value + mpp.Delete = value + mpp.ManageAttachedServers = value + mpp.ManagePorts = value + mpp.ManageProcesses = value + mpp.ModifyResources = value + mpp.SetDescription = value + mpp.SetEmail = value + mpp.SetName = value + mpp.Show = value +} + +// Sets all private networks' permissions +func (pnp *PrivateNetworkPerm) SetAll(value bool) { + pnp.Create = value + pnp.Delete = value + pnp.ManageAttachedServers = value + pnp.SetDescription = value + pnp.SetName = value + pnp.SetNetworkInfo = value + pnp.Show = value +} + +// Sets all roles' permissions +func (rp *RolePerm) SetAll(value bool) { + rp.Clone = value + rp.Create = value + rp.Delete = value + rp.ManageUsers = value + rp.Modify = value + rp.SetDescription = value + rp.SetName = value + rp.Show = value +} + +// Sets all servers' permissions +func (sp *ServerPerm) SetAll(value bool) { + sp.AccessKVMConsole = value + sp.AssignIP = value + sp.Clone = value + sp.Create = value + sp.Delete = value + sp.ManageDVD = value + sp.ManageSnapshot = value + sp.Reinstall = value + sp.Resize = value + sp.Restart = value + sp.SetDescription = value + sp.SetName = value + sp.Show = value + sp.Shutdown = value + sp.Start = value +} + +// Sets all shared storages' permissions +func (ssp *SharedStoragePerm) SetAll(value bool) { + ssp.Access = value + ssp.Create = value + ssp.Delete = value + ssp.ManageAttachedServers = value + ssp.Resize = value + ssp.SetDescription = value + ssp.SetName = value + ssp.Show = value +} + +// Sets all usages' permissions +func (up *UsagePerm) SetAll(value bool) { + up.Show = value +} + +// Sets all users' permissions +func (up *UserPerm) SetAll(value bool) { + up.ChangeRole = value + up.Create = value + up.Delete = value + up.Disable = value + up.Enable = value + up.ManageAPI = value + up.SetDescription = value + up.SetEmail = value + up.SetPassword = value + up.Show = value +} + +// Sets all VPNs' permissions +func (vpnp *VPNPerm) SetAll(value bool) { + vpnp.Create = value + vpnp.Delete = value + vpnp.DownloadFile = value + vpnp.SetDescription = value + vpnp.SetName = value + vpnp.Show = value +} + +// Sets all available permissions +func (p *Permissions) SetAll(v bool) { + if p.Backups == nil { + p.Backups = &BackupPerm{v, v, v} + } else { + p.Backups.SetAll(v) + } + if p.Firewalls == nil { + p.Firewalls = &FirewallPerm{v, v, v, v, v, v, v, v} + } else { + p.Firewalls.SetAll(v) + } + if p.Images == nil { + p.Images = &ImagePerm{v, v, v, v, v, v} + } else { + p.Images.SetAll(v) + } + if p.Invoice == nil { + p.Invoice = &InvoicePerm{v} + } else { + p.Invoice.SetAll(v) + } + if p.IPs == nil { + p.IPs = &IPPerm{v, v, v, v, v} + } else { + p.IPs.SetAll(v) + } + if p.LoadBalancers == nil { + p.LoadBalancers = &LoadBalancerPerm{v, v, v, v, v, v, v, v} + } else { + p.LoadBalancers.SetAll(v) + } + if p.Logs == nil { + p.Logs = &LogPerm{v} + } else { + p.Logs.SetAll(v) + } + if p.MonitorCenter == nil { + p.MonitorCenter = &MonitorCenterPerm{v} + } else { + p.MonitorCenter.SetAll(v) + } + if p.MonitorPolicies == nil { + p.MonitorPolicies = &MonitorPolicyPerm{v, v, v, v, v, v, v, v, v, v, v} + } else { + p.MonitorPolicies.SetAll(v) + } + if p.PrivateNetworks == nil { + p.PrivateNetworks = &PrivateNetworkPerm{v, v, v, v, v, v, v} + } else { + p.PrivateNetworks.SetAll(v) + } + if p.Roles == nil { + p.Roles = &RolePerm{v, v, v, v, v, v, v, v} + } else { + p.Roles.SetAll(v) + } + if p.Servers == nil { + p.Servers = &ServerPerm{v, v, v, v, v, v, v, v, v, v, v, v, v, v, v} + } else { + p.Servers.SetAll(v) + } + if p.SharedStorage == nil { + p.SharedStorage = &SharedStoragePerm{v, v, v, v, v, v, v, v} + } else { + p.SharedStorage.SetAll(v) + } + if p.Usages == nil { + p.Usages = &UsagePerm{v} + } else { + p.Usages.SetAll(v) + } + if p.Users == nil { + p.Users = &UserPerm{v, v, v, v, v, v, v, v, v, v} + } else { + p.Users.SetAll(v) + } + if p.VPNs == nil { + p.VPNs = &VPNPerm{v, v, v, v, v, v} + } else { + p.VPNs.SetAll(v) + } +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/serverappliances.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/serverappliances.go new file mode 100644 index 000000000..03c45f3d8 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/serverappliances.go @@ -0,0 +1,48 @@ +package oneandone + +import "net/http" + +type ServerAppliance struct { + Identity + typeField + OsInstallBase string `json:"os_installation_base,omitempty"` + OsFamily string `json:"os_family,omitempty"` + Os string `json:"os,omitempty"` + OsVersion string `json:"os_version,omitempty"` + Version string `json:"version,omitempty"` + MinHddSize int `json:"min_hdd_size"` + Architecture interface{} `json:"os_architecture"` + Licenses interface{} `json:"licenses,omitempty"` + Categories []string `json:"categories,omitempty"` + // AvailableDatacenters []string `json:"available_datacenters,omitempty"` + ApiPtr +} + +// GET /server_appliances +func (api *API) ListServerAppliances(args ...interface{}) ([]ServerAppliance, error) { + url, err := processQueryParams(createUrl(api, serverAppliancePathSegment), args...) + if err != nil { + return nil, err + } + res := []ServerAppliance{} + err = api.Client.Get(url, &res, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range res { + res[index].api = api + } + return res, nil +} + +// GET /server_appliances/{id} +func (api *API) GetServerAppliance(sa_id string) (*ServerAppliance, error) { + res := new(ServerAppliance) + url := createUrl(api, serverAppliancePathSegment, sa_id) + err := api.Client.Get(url, &res, http.StatusOK) + if err != nil { + return nil, err + } + // res.api = api + return res, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/servers.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/servers.go new file mode 100644 index 000000000..18fad51a2 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/servers.go @@ -0,0 +1,808 @@ +package oneandone + +import ( + "encoding/json" + "errors" + "math/big" + "net/http" +) + +type Server struct { + ApiPtr + Identity + descField + CloudPanelId string `json:"cloudpanel_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + FirstPassword string `json:"first_password,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + Status *Status `json:"status,omitempty"` + Hardware *Hardware `json:"hardware,omitempty"` + Image *Identity `json:"image,omitempty"` + Dvd *Identity `json:"dvd,omitempty"` + MonPolicy *Identity `json:"monitoring_policy,omitempty"` + Snapshot *ServerSnapshot `json:"snapshot,omitempty"` + Ips []ServerIp `json:"ips,omitempty"` + PrivateNets []Identity `json:"private_networks,omitempty"` + Alerts *ServerAlerts `json:"-"` + AlertsRaw *json.RawMessage `json:"alerts,omitempty"` +} + +type Hardware struct { + Vcores int `json:"vcore,omitempty"` + CoresPerProcessor int `json:"cores_per_processor"` + Ram float32 `json:"ram"` + Hdds []Hdd `json:"hdds,omitempty"` + FixedInsSizeId string `json:"fixed_instance_size_id,omitempty"` + ApiPtr +} + +type ServerHdds struct { + Hdds []Hdd `json:"hdds,omitempty"` +} + +type Hdd struct { + idField + Size int `json:"size,omitempty"` + IsMain bool `json:"is_main,omitempty"` + ApiPtr +} + +type serverDeployImage struct { + idField + Password string `json:"password,omitempty"` + Firewall *Identity `json:"firewall_policy,omitempty"` +} + +type ServerIp struct { + idField + typeField + Ip string `json:"ip,omitempty"` + ReverseDns string `json:"reverse_dns,omitempty"` + Firewall *Identity `json:"firewall_policy,omitempty"` + LoadBalancers []Identity `json:"load_balancers,omitempty"` + ApiPtr +} + +type ServerIpInfo struct { + idField // IP id + Ip string `json:"ip,omitempty"` + ServerName string `json:"server_name,omitempty"` +} + +type ServerSnapshot struct { + idField + CreationDate string `json:"creation_date,omitempty"` + DeletionDate string `json:"deletion_date,omitempty"` +} + +type ServerAlerts struct { + AlertSummary []serverAlertSummary + AlertDetails *serverAlertDetails +} + +type serverAlertSummary struct { + countField + typeField +} + +type serverAlertDetails struct { + Criticals []ServerAlert `json:"critical,omitempty"` + Warnings []ServerAlert `json:"warning,omitempty"` +} + +type ServerAlert struct { + typeField + descField + Date string `json:"date"` +} + +type ServerRequest struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Hardware Hardware `json:"hardware"` + ApplianceId string `json:"appliance_id,omitempty"` + Password string `json:"password,omitempty"` + PowerOn bool `json:"power_on"` + FirewallPolicyId string `json:"firewall_policy_id,omitempty"` + IpId string `json:"ip_id,omitempty"` + LoadBalancerId string `json:"load_balancer_id,omitempty"` + MonitoringPolicyId string `json:"monitoring_policy_id,omitempty"` + DatacenterId string `json:"datacenter_id,omitempty"` + SSHKey string `json:"rsa_key,omitempty"` +} + +type ServerAction struct { + Action string `json:"action,omitempty"` + Method string `json:"method,omitempty"` +} + +type FixedInstanceInfo struct { + Identity + Hardware *Hardware `json:"hardware,omitempty"` + ApiPtr +} + +// GET /servers +func (api *API) ListServers(args ...interface{}) ([]Server, error) { + url, err := processQueryParams(createUrl(api, serverPathSegment), args...) + if err != nil { + return nil, err + } + result := []Server{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for _, s := range result { + s.api = api + s.decodeRaws() + } + return result, nil +} + +// POST /servers +func (api *API) CreateServer(request *ServerRequest) (string, *Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment) + insert2map := func(hasht map[string]interface{}, key string, value string) { + if key != "" && value != "" { + hasht[key] = value + } + } + req := make(map[string]interface{}) + hw := make(map[string]interface{}) + req["name"] = request.Name + req["description"] = request.Description + req["appliance_id"] = request.ApplianceId + req["power_on"] = request.PowerOn + insert2map(req, "password", request.Password) + insert2map(req, "firewall_policy_id", request.FirewallPolicyId) + insert2map(req, "ip_id", request.IpId) + insert2map(req, "load_balancer_id", request.LoadBalancerId) + insert2map(req, "monitoring_policy_id", request.MonitoringPolicyId) + insert2map(req, "datacenter_id", request.DatacenterId) + insert2map(req, "rsa_key", request.SSHKey) + req["hardware"] = hw + if request.Hardware.FixedInsSizeId != "" { + hw["fixed_instance_size_id"] = request.Hardware.FixedInsSizeId + } else { + hw["vcore"] = request.Hardware.Vcores + hw["cores_per_processor"] = request.Hardware.CoresPerProcessor + hw["ram"] = request.Hardware.Ram + hw["hdds"] = request.Hardware.Hdds + } + err := api.Client.Post(url, &req, &result, http.StatusAccepted) + if err != nil { + return "", nil, err + } + result.api = api + result.decodeRaws() + return result.Id, result, nil +} + +// This is a wraper function for `CreateServer` that returns the server's IP address and first password. +// The function waits at most `timeout` seconds for the server to be created. +// The initial `POST /servers` response does not contain the IP address, so we need to wait +// until the server is created. +func (api *API) CreateServerEx(request *ServerRequest, timeout int) (string, string, error) { + id, server, err := api.CreateServer(request) + if server != nil && err == nil { + count := timeout / 5 + if request.PowerOn { + err = api.WaitForState(server, "POWERED_ON", 5, count) + } else { + err = api.WaitForState(server, "POWERED_OFF", 5, count) + } + if err != nil { + return "", "", err + } + server, err := api.GetServer(id) + if server != nil && err == nil && server.Ips[0].Ip != "" { + if server.FirstPassword != "" { + return server.Ips[0].Ip, server.FirstPassword, nil + } + if request.Password != "" { + return server.Ips[0].Ip, request.Password, nil + } + // should never reach here + return "", "", errors.New("No server's password was found.") + } + } + return "", "", err +} + +// GET /servers/{id} +func (api *API) GetServer(server_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/fixed_instance_sizes +func (api *API) ListFixedInstanceSizes() ([]FixedInstanceInfo, error) { + result := []FixedInstanceInfo{} + url := createUrl(api, serverPathSegment, "fixed_instance_sizes") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// GET /servers/fixed_instance_sizes/{fixed_instance_size_id} +func (api *API) GetFixedInstanceSize(fis_id string) (*FixedInstanceInfo, error) { + result := new(FixedInstanceInfo) + url := createUrl(api, serverPathSegment, "fixed_instance_sizes", fis_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /servers/{id} +func (api *API) DeleteServer(server_id string, keep_ips bool) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id) + pm := make(map[string]interface{}, 1) + pm["keep_ips"] = keep_ips + url = appendQueryParams(url, pm) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{id} +func (api *API) RenameServer(server_id string, new_name string, new_desc string) (*Server, error) { + data := struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + }{Name: new_name, Description: new_desc} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id) + err := api.Client.Put(url, &data, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{server_id}/hardware +func (api *API) GetServerHardware(server_id string) (*Hardware, error) { + result := new(Hardware) + url := createUrl(api, serverPathSegment, server_id, "hardware") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /servers/{server_id}/hardware +func (api *API) UpdateServerHardware(server_id string, hardware *Hardware) (*Server, error) { + var vc, cpp *int + var ram *float32 + if hardware.Vcores > 0 { + vc = new(int) + *vc = hardware.Vcores + } + if hardware.CoresPerProcessor > 0 { + cpp = new(int) + *cpp = hardware.CoresPerProcessor + } + if big.NewFloat(float64(hardware.Ram)).Cmp(big.NewFloat(0)) != 0 { + ram = new(float32) + *ram = hardware.Ram + } + req := struct { + VCores *int `json:"vcore,omitempty"` + Cpp *int `json:"cores_per_processor,omitempty"` + Ram *float32 `json:"ram,omitempty"` + Flavor string `json:"fixed_instance_size_id,omitempty"` + }{VCores: vc, Cpp: cpp, Ram: ram, Flavor: hardware.FixedInsSizeId} + + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "hardware") + err := api.Client.Put(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/hardware/hdds +func (api *API) ListServerHdds(server_id string) ([]Hdd, error) { + result := []Hdd{} + url := createUrl(api, serverPathSegment, server_id, "hardware/hdds") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /servers/{id}/hardware/hdds +func (api *API) AddServerHdds(server_id string, hdds *ServerHdds) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "hardware/hdds") + err := api.Client.Post(url, &hdds, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/hardware/hdds/{id} +func (api *API) GetServerHdd(server_id string, hdd_id string) (*Hdd, error) { + result := new(Hdd) + url := createUrl(api, serverPathSegment, server_id, "hardware/hdds", hdd_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /servers/{id}/hardware/hdds/{id} +func (api *API) DeleteServerHdd(server_id string, hdd_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "hardware/hdds", hdd_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{id}/hardware/hdds/{id} +func (api *API) ResizeServerHdd(server_id string, hdd_id string, new_size int) (*Server, error) { + data := Hdd{Size: new_size} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "hardware/hdds", hdd_id) + err := api.Client.Put(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/image +func (api *API) GetServerImage(server_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, serverPathSegment, server_id, "image") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /servers/{id}/image +func (api *API) ReinstallServerImage(server_id string, image_id string, password string, fp_id string) (*Server, error) { + data := new(serverDeployImage) + data.Id = image_id + data.Password = password + if fp_id != "" { + fp := new(Identity) + fp.Id = fp_id + data.Firewall = fp + } + + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "image") + err := api.Client.Put(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/ips +func (api *API) ListServerIps(server_id string) ([]ServerIp, error) { + result := []ServerIp{} + url := createUrl(api, serverPathSegment, server_id, "ips") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /servers/{id}/ips +func (api *API) AssignServerIp(server_id string, ip_type string) (*Server, error) { + data := typeField{Type: ip_type} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips") + err := api.Client.Post(url, &data, &result, http.StatusCreated) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/ips/{id} +func (api *API) GetServerIp(server_id string, ip_id string) (*ServerIp, error) { + result := new(ServerIp) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /servers/{id}/ips/{id} +func (api *API) DeleteServerIp(server_id string, ip_id string, keep_ip bool) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id) + qm := make(map[string]interface{}, 1) + qm["keep_ip"] = keep_ip + url = appendQueryParams(url, qm) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /servers/{id}/status +func (api *API) GetServerStatus(server_id string) (*Status, error) { + result := new(Status) + url := createUrl(api, serverPathSegment, server_id, "status") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /servers/{id}/status/action (action = REBOOT) +func (api *API) RebootServer(server_id string, is_hardware bool) (*Server, error) { + result := new(Server) + request := ServerAction{} + request.Action = "REBOOT" + if is_hardware { + request.Method = "HARDWARE" + } else { + request.Method = "SOFTWARE" + } + url := createUrl(api, serverPathSegment, server_id, "status", "action") + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{id}/status/action (action = POWER_OFF) +func (api *API) ShutdownServer(server_id string, is_hardware bool) (*Server, error) { + result := new(Server) + request := ServerAction{} + request.Action = "POWER_OFF" + if is_hardware { + request.Method = "HARDWARE" + } else { + request.Method = "SOFTWARE" + } + url := createUrl(api, serverPathSegment, server_id, "status", "action") + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{id}/status/action (action = POWER_ON) +func (api *API) StartServer(server_id string) (*Server, error) { + result := new(Server) + request := ServerAction{} + request.Action = "POWER_ON" + url := createUrl(api, serverPathSegment, server_id, "status", "action") + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/dvd +func (api *API) GetServerDvd(server_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, serverPathSegment, server_id, "dvd") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /servers/{id}/dvd +func (api *API) EjectServerDvd(server_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "dvd") + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{id}/dvd +func (api *API) LoadServerDvd(server_id string, dvd_id string) (*Server, error) { + request := Identity{} + request.Id = dvd_id + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "dvd") + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/private_networks +func (api *API) ListServerPrivateNetworks(server_id string) ([]Identity, error) { + result := []Identity{} + url := createUrl(api, serverPathSegment, server_id, "private_networks") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /servers/{id}/private_networks +func (api *API) AssignServerPrivateNetwork(server_id string, pn_id string) (*Server, error) { + req := new(Identity) + req.Id = pn_id + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "private_networks") + err := api.Client.Post(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/private_networks/{id} +func (api *API) GetServerPrivateNetwork(server_id string, pn_id string) (*PrivateNetwork, error) { + result := new(PrivateNetwork) + url := createUrl(api, serverPathSegment, server_id, "private_networks", pn_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /servers/{id}/private_networks/{id} +func (api *API) RemoveServerPrivateNetwork(server_id string, pn_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "private_networks", pn_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{server_id}/ips/{ip_id}/load_balancers +func (api *API) ListServerIpLoadBalancers(server_id string, ip_id string) ([]Identity, error) { + result := []Identity{} + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "load_balancers") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /servers/{server_id}/ips/{ip_id}/load_balancers +func (api *API) AssignServerIpLoadBalancer(server_id string, ip_id string, lb_id string) (*Server, error) { + req := struct { + LbId string `json:"load_balancer_id"` + }{lb_id} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "load_balancers") + err := api.Client.Post(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// DELETE /servers/{server_id}/ips/{ip_id}/load_balancers +func (api *API) UnassignServerIpLoadBalancer(server_id string, ip_id string, lb_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "load_balancers", lb_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{server_id}/ips/{ip_id}/firewall_policy +func (api *API) GetServerIpFirewallPolicy(server_id string, ip_id string) (*Identity, error) { + result := new(Identity) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "firewall_policy") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /servers/{server_id}/ips/{ip_id}/firewall_policy +func (api *API) AssignServerIpFirewallPolicy(server_id string, ip_id string, fp_id string) (*Server, error) { + req := idField{fp_id} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "firewall_policy") + err := api.Client.Put(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// DELETE /servers/{server_id}/ips/{ip_id}/firewall_policy +func (api *API) UnassignServerIpFirewallPolicy(server_id string, ip_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "ips", ip_id, "firewall_policy") + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// GET /servers/{id}/snapshots +func (api *API) GetServerSnapshot(server_id string) (*ServerSnapshot, error) { + result := new(ServerSnapshot) + url := createUrl(api, serverPathSegment, server_id, "snapshots") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /servers/{id}/snapshots +func (api *API) CreateServerSnapshot(server_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "snapshots") + err := api.Client.Post(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// PUT /servers/{server_id}/snapshots/{snapshot_id} +func (api *API) RestoreServerSnapshot(server_id string, snapshot_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "snapshots", snapshot_id) + err := api.Client.Put(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// DELETE /servers/{server_id}/snapshots/{snapshot_id} +func (api *API) DeleteServerSnapshot(server_id string, snapshot_id string) (*Server, error) { + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "snapshots", snapshot_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +// POST /servers/{server_id}/clone +func (api *API) CloneServer(server_id string, new_name string, datacenter_id string) (*Server, error) { + data := struct { + Name string `json:"name"` + DatacenterId string `json:"datacenter_id,omitempty"` + }{Name: new_name, DatacenterId: datacenter_id} + result := new(Server) + url := createUrl(api, serverPathSegment, server_id, "clone") + err := api.Client.Post(url, &data, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + result.decodeRaws() + return result, nil +} + +func (s *Server) GetState() (string, error) { + st, err := s.api.GetServerStatus(s.Id) + if st == nil { + return "", err + } + return st.State, err +} + +func (server *Server) decodeRaws() { + if server.AlertsRaw != nil { + server.Alerts = new(ServerAlerts) + var sad serverAlertDetails + if err := json.Unmarshal(*server.AlertsRaw, &sad); err == nil { + server.Alerts.AlertDetails = &sad + return + } + var sams []serverAlertSummary + if err := json.Unmarshal(*server.AlertsRaw, &sams); err == nil { + server.Alerts.AlertSummary = sams + } + } +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/setup.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/setup.go new file mode 100644 index 000000000..7d910c653 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/setup.go @@ -0,0 +1,19 @@ +package oneandone + +// The base url for 1&1 Cloud Server REST API. +var BaseUrl = "https://cloudpanel-api.1and1.com/v1" + +// Authentication token +var Token string + +// SetBaseUrl is intended to set the REST base url. BaseUrl is declared in setup.go +func SetBaseUrl(newbaseurl string) string { + BaseUrl = newbaseurl + return BaseUrl +} + +// SetToken is used to set authentication Token for the REST service. Token is declared in setup.go +func SetToken(newtoken string) string { + Token = newtoken + return Token +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/sharedstorages.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/sharedstorages.go new file mode 100644 index 000000000..fdb2a7bfd --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/sharedstorages.go @@ -0,0 +1,190 @@ +package oneandone + +import ( + "net/http" +) + +type SharedStorage struct { + Identity + descField + Size int `json:"size"` + MinSizeAllowed int `json:"minimum_size_allowed"` + SizeUsed string `json:"size_used,omitempty"` + State string `json:"state,omitempty"` + CloudPanelId string `json:"cloudpanel_id,omitempty"` + SiteId string `json:"site_id,omitempty"` + CifsPath string `json:"cifs_path,omitempty"` + NfsPath string `json:"nfs_path,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + Servers []SharedStorageServer `json:"servers,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + ApiPtr +} + +type SharedStorageServer struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Rights string `json:"rights,omitempty"` +} + +type SharedStorageRequest struct { + DatacenterId string `json:"datacenter_id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Size *int `json:"size"` +} + +type SharedStorageAccess struct { + State string `json:"state,omitempty"` + KerberosContentFile string `json:"kerberos_content_file,omitempty"` + UserDomain string `json:"user_domain,omitempty"` + SiteId string `json:"site_id,omitempty"` + NeedsPasswordReset int `json:"needs_password_reset"` +} + +// GET /shared_storages +func (api *API) ListSharedStorages(args ...interface{}) ([]SharedStorage, error) { + url, err := processQueryParams(createUrl(api, sharedStoragePathSegment), args...) + if err != nil { + return nil, err + } + result := []SharedStorage{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /shared_storages +func (api *API) CreateSharedStorage(request *SharedStorageRequest) (string, *SharedStorage, error) { + result := new(SharedStorage) + url := createUrl(api, sharedStoragePathSegment) + err := api.Client.Post(url, request, &result, http.StatusAccepted) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /shared_storages/{id} +func (api *API) GetSharedStorage(ss_id string) (*SharedStorage, error) { + result := new(SharedStorage) + url := createUrl(api, sharedStoragePathSegment, ss_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /shared_storages/{id} +func (api *API) DeleteSharedStorage(ss_id string) (*SharedStorage, error) { + result := new(SharedStorage) + url := createUrl(api, sharedStoragePathSegment, ss_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /shared_storages/{id} +func (api *API) UpdateSharedStorage(ss_id string, request *SharedStorageRequest) (*SharedStorage, error) { + result := new(SharedStorage) + url := createUrl(api, sharedStoragePathSegment, ss_id) + err := api.Client.Put(url, &request, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /shared_storages/{id}/servers +func (api *API) ListSharedStorageServers(st_id string) ([]SharedStorageServer, error) { + result := []SharedStorageServer{} + url := createUrl(api, sharedStoragePathSegment, st_id, "servers") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /shared_storages/{id}/servers +func (api *API) AddSharedStorageServers(st_id string, servers []SharedStorageServer) (*SharedStorage, error) { + result := new(SharedStorage) + req := struct { + Servers []SharedStorageServer `json:"servers"` + }{servers} + url := createUrl(api, sharedStoragePathSegment, st_id, "servers") + err := api.Client.Post(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /shared_storages/{id}/servers/{id} +func (api *API) GetSharedStorageServer(st_id string, ser_id string) (*SharedStorageServer, error) { + result := new(SharedStorageServer) + url := createUrl(api, sharedStoragePathSegment, st_id, "servers", ser_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// DELETE /shared_storages/{id}/servers/{id} +func (api *API) DeleteSharedStorageServer(st_id string, ser_id string) (*SharedStorage, error) { + result := new(SharedStorage) + url := createUrl(api, sharedStoragePathSegment, st_id, "servers", ser_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /shared_storages/access +func (api *API) GetSharedStorageCredentials() ([]SharedStorageAccess, error) { + result := []SharedStorageAccess{} + url := createUrl(api, sharedStoragePathSegment, "access") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /shared_storages/access +func (api *API) UpdateSharedStorageCredentials(new_pass string) ([]SharedStorageAccess, error) { + result := []SharedStorageAccess{} + req := struct { + Password string `json:"password"` + }{new_pass} + url := createUrl(api, sharedStoragePathSegment, "access") + err := api.Client.Put(url, &req, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + return result, nil +} + +func (ss *SharedStorage) GetState() (string, error) { + in, err := ss.api.GetSharedStorage(ss.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/usages.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/usages.go new file mode 100644 index 000000000..e56c9f2ef --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/usages.go @@ -0,0 +1,52 @@ +package oneandone + +import ( + "net/http" + "time" +) + +type Usages struct { + Images []usage `json:"IMAGES,omitempty"` + LoadBalancers []usage `json:"LOAD BALANCERS,omitempty"` + PublicIPs []usage `json:"PUBLIC IP,omitempty"` + Servers []usage `json:"SERVERS,omitempty"` + SharedStorages []usage `json:"SHARED STORAGE,omitempty"` + ApiPtr +} + +type usage struct { + Identity + Site int `json:"site"` + Services []usageService `json:"services,omitempty"` +} + +type usageService struct { + AverageAmmount string `json:"avg_amount,omitempty"` + Unit string `json:"unit,omitempty"` + Usage int `json:"usage"` + Details []usageDetails `json:"detail,omitempty"` + typeField +} + +type usageDetails struct { + AverageAmmount string `json:"avg_amount,omitempty"` + StartDate string `json:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty"` + Unit string `json:"unit,omitempty"` + Usage int `json:"usage,omitempty"` +} + +// GET /usages +func (api *API) ListUsages(period string, sd *time.Time, ed *time.Time, args ...interface{}) (*Usages, error) { + result := new(Usages) + url, err := processQueryParamsExt(createUrl(api, usagePathSegment), period, sd, ed, args...) + if err != nil { + return nil, err + } + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/users.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/users.go new file mode 100644 index 000000000..782d07a50 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/users.go @@ -0,0 +1,205 @@ +package oneandone + +import "net/http" + +type User struct { + Identity + descField + CreationDate string `json:"creation_date,omitempty"` + Email string `json:"email,omitempty"` + State string `json:"state,omitempty"` + Role *Identity `json:"role,omitempty"` + Api *UserApi `json:"api,omitempty"` + ApiPtr +} + +type UserApi struct { + Active bool `json:"active"` + AllowedIps []string `json:"allowed_ips,omitempty"` + UserApiKey + ApiPtr +} + +type UserApiKey struct { + Key string `json:"key,omitempty"` +} + +type UserRequest struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Password string `json:"password,omitempty"` + Email string `json:"email,omitempty"` + State string `json:"state,omitempty"` +} + +// GET /users +func (api *API) ListUsers(args ...interface{}) ([]User, error) { + url, err := processQueryParams(createUrl(api, userPathSegment), args...) + if err != nil { + return nil, err + } + result := []User{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for index, _ := range result { + result[index].api = api + } + return result, nil +} + +// POST /users +func (api *API) CreateUser(user *UserRequest) (string, *User, error) { + result := new(User) + url := createUrl(api, userPathSegment) + err := api.Client.Post(url, &user, &result, http.StatusCreated) + if err != nil { + return "", nil, err + } + result.api = api + return result.Id, result, nil +} + +// GET /users/{id} +func (api *API) GetUser(user_id string) (*User, error) { + result := new(User) + url := createUrl(api, userPathSegment, user_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /users/{id} +func (api *API) DeleteUser(user_id string) (*User, error) { + result := new(User) + url := createUrl(api, userPathSegment, user_id) + err := api.Client.Delete(url, nil, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /users/{id} +func (api *API) ModifyUser(user_id string, user *UserRequest) (*User, error) { + result := new(User) + url := createUrl(api, userPathSegment, user_id) + err := api.Client.Put(url, &user, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /users/{id}/api +func (api *API) GetUserApi(user_id string) (*UserApi, error) { + result := new(UserApi) + url := createUrl(api, userPathSegment, user_id, "api") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /users/{id}/api +func (api *API) ModifyUserApi(user_id string, active bool) (*User, error) { + result := new(User) + req := struct { + Active bool `json:"active"` + }{active} + url := createUrl(api, userPathSegment, user_id, "api") + err := api.Client.Put(url, &req, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /users/{id}/api/key +func (api *API) GetUserApiKey(user_id string) (*UserApiKey, error) { + result := new(UserApiKey) + url := createUrl(api, userPathSegment, user_id, "api/key") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// PUT /users/{id}/api/key +func (api *API) RenewUserApiKey(user_id string) (*User, error) { + result := new(User) + url := createUrl(api, userPathSegment, user_id, "api/key") + err := api.Client.Put(url, nil, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /users/{id}/api/ips +func (api *API) ListUserApiAllowedIps(user_id string) ([]string, error) { + result := []string{} + url := createUrl(api, userPathSegment, user_id, "api/ips") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +// POST /users/{id}/api/ips +func (api *API) AddUserApiAlowedIps(user_id string, ips []string) (*User, error) { + result := new(User) + req := struct { + Ips []string `json:"ips"` + }{ips} + url := createUrl(api, userPathSegment, user_id, "api/ips") + err := api.Client.Post(url, &req, &result, http.StatusCreated) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /users/{id}/api/ips/{ip} +func (api *API) RemoveUserApiAllowedIp(user_id string, ip string) (*User, error) { + result := new(User) + url := createUrl(api, userPathSegment, user_id, "api/ips", ip) + err := api.Client.Delete(url, nil, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /users/{id}/api/ips +func (api *API) GetCurrentUserPermissions() (*Permissions, error) { + result := new(Permissions) + url := createUrl(api, userPathSegment, "current_user_permissions") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + return result, nil +} + +func (u *User) GetState() (string, error) { + in, err := u.api.GetUser(u.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/vpns.go b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/vpns.go new file mode 100644 index 000000000..723a85459 --- /dev/null +++ b/vendor/github.com/1and1/oneandone-cloudserver-sdk-go/vpns.go @@ -0,0 +1,114 @@ +package oneandone + +import "net/http" + +type VPN struct { + Identity + descField + typeField + CloudPanelId string `json:"cloudpanel_id,omitempty"` + CreationDate string `json:"creation_date,omitempty"` + State string `json:"state,omitempty"` + IPs []string `json:"ips,omitempty"` + Datacenter *Datacenter `json:"datacenter,omitempty"` + ApiPtr +} + +type configZipFile struct { + Base64String string `json:"config_zip_file"` +} + +// GET /vpns +func (api *API) ListVPNs(args ...interface{}) ([]VPN, error) { + url, err := processQueryParams(createUrl(api, vpnPathSegment), args...) + if err != nil { + return nil, err + } + result := []VPN{} + err = api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + for _, vpn := range result { + vpn.api = api + } + return result, nil +} + +// POST /vpns +func (api *API) CreateVPN(name string, description string, datacenter_id string) (string, *VPN, error) { + res := new(VPN) + url := createUrl(api, vpnPathSegment) + req := struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + DatacenterId string `json:"datacenter_id,omitempty"` + }{Name: name, Description: description, DatacenterId: datacenter_id} + err := api.Client.Post(url, &req, &res, http.StatusAccepted) + if err != nil { + return "", nil, err + } + res.api = api + return res.Id, res, nil +} + +// GET /vpns/{vpn_id} +func (api *API) GetVPN(vpn_id string) (*VPN, error) { + result := new(VPN) + url := createUrl(api, vpnPathSegment, vpn_id) + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// PUT /vpns/{vpn_id} +func (api *API) ModifyVPN(vpn_id string, name string, description string) (*VPN, error) { + result := new(VPN) + url := createUrl(api, vpnPathSegment, vpn_id) + req := struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + }{Name: name, Description: description} + err := api.Client.Put(url, &req, &result, http.StatusOK) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// DELETE /vpns/{vpn_id} +func (api *API) DeleteVPN(vpn_id string) (*VPN, error) { + result := new(VPN) + url := createUrl(api, vpnPathSegment, vpn_id) + err := api.Client.Delete(url, nil, &result, http.StatusAccepted) + if err != nil { + return nil, err + } + result.api = api + return result, nil +} + +// GET /vpns/{vpn_id}/configuration_file +// Returns VPN configuration files (in a zip arhive) as a base64 encoded string +func (api *API) GetVPNConfigFile(vpn_id string) (string, error) { + result := new(configZipFile) + url := createUrl(api, vpnPathSegment, vpn_id, "configuration_file") + err := api.Client.Get(url, &result, http.StatusOK) + if err != nil { + return "", err + } + + return result.Base64String, nil +} + +func (vpn *VPN) GetState() (string, error) { + in, err := vpn.api.GetVPN(vpn.Id) + if in == nil { + return "", err + } + return in.State, err +} diff --git a/vendor/vendor.json b/vendor/vendor.json index f56d8a8bf..51052b081 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -14,6 +14,12 @@ "revision": "81b7822b1e798e8f17bf64b59512a5be4097e966", "revisionTime": "2017-01-18T16:13:56Z" }, + { + "checksumSHA1": "aABATU51PlDHfGeSe5cc9udwSXg=", + "path": "github.com/1and1/oneandone-cloudserver-sdk-go", + "revision": "5678f03fc801525df794f953aa82f5ad7555a2ef", + "revisionTime": "2016-08-11T22:04:02Z" + }, { "checksumSHA1": "N92Zji40JkCHAnsCNHTP4iKPz88=", "comment": "v2.1.1-beta-8-gca4d906", diff --git a/website/source/docs/providers/oneandone/index.html.markdown b/website/source/docs/providers/oneandone/index.html.markdown new file mode 100644 index 000000000..6a215ee36 --- /dev/null +++ b/website/source/docs/providers/oneandone/index.html.markdown @@ -0,0 +1,54 @@ +--- +layout: "oneandone" +page_title: "Provider: 1&1" +sidebar_current: "docs-oneandone-index" +description: |- + A provider for 1&1. +--- + +# 1&1 Provider + +The 1&1 provider gives the ability to deploy and configure resources using the 1&1 Cloud Server API. + +Use the navigation to the left to read about the available resources. + + +## Usage + +The provider needs to be configured with proper credentials before it can be used. + + +``` +$ export ONEANDONE_TOKEN="oneandone_token" +``` + +Or you can provide your credentials like this: + + +The credentials provided in `.tf` file will override credentials in the environment variables. + +## Example Usage + + +``` +provider "oneandone"{ + token = "oneandone_token" + endpoint = "oneandone_endpoint" + retries = 100 +} + +resource "oneandone_server" "server" { + # ... +} +``` + + +## Configuration Reference + +The following arguments are supported: + +* `token` - (Required) If omitted, the `ONEANDONE_TOKEN` environment variable is used. + +* `endpoint` - (Optional) + +* `retries` - (Optional) Number of retries while waiting for a resource to be provisioned. Default value is 50. diff --git a/website/source/docs/providers/oneandone/r/oneandone_firewall_policy.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_firewall_policy.html.markdown new file mode 100644 index 000000000..594ef7ffa --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_firewall_policy.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_firewall_policy" +sidebar_current: "docs-oneandone-resource-firwall-policy" +description: |- + Creates and manages 1&1 Firewall Policy. +--- + +# oneandone\_server + +Manages a Firewall Policy on 1&1 + +## Example Usage + +``` +resource "oneandone_firewall_policy" "fw" { + name = "test_fw_011" + rules = [ + { + "protocol" = "TCP" + "port_from" = 80 + "port_to" = 80 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "ICMP" + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 43 + "port_to" = 43 + "source_ip" = "0.0.0.0" + }, + { + "protocol" = "TCP" + "port_from" = 22 + "port_to" = 22 + "source_ip" = "0.0.0.0" + } + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `description` - (Optional) [string] Description for the VPN +* `name` - (Required) [string] The name of the VPN. + +Firewall Policy Rules (`rules`) support the follwing: + +* `protocol` - (Required) [String] The protocol for the rule ["TCP", "UDP", "TCP/UDP", "ICMP", "IPSEC"] +* `port_from` - (Optional) [String] Defines the start range of the allowed port +* `port_to` - (Optional) [String] Defines the end range of the allowed port +* `source_ip` - (Optional) [String] Only traffic directed to the respective IP address + diff --git a/website/source/docs/providers/oneandone/r/oneandone_loadbalancer.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_loadbalancer.html.markdown new file mode 100644 index 000000000..e77d66169 --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_loadbalancer.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_loadbalancer" +sidebar_current: "docs-oneandone-resource-loadbalancer" +description: |- + Creates and manages 1&1 Load Balancer. +--- + +# oneandone\_server + +Manages a Load Balancer on 1&1 + +## Example Usage + +``` +resource "oneandone_loadbalancer" "lb" { + name = "test_lb" + method = "ROUND_ROBIN" + persistence = true + persistence_time = 60 + health_check_test = "TCP" + health_check_interval = 300 + datacenter = "GB" + rules = [ + { + protocol = "TCP" + port_balancer = 8080 + port_server = 8089 + source_ip = "0.0.0.0" + }, + { + protocol = "TCP" + port_balancer = 9090 + port_server = 9099 + source_ip = "0.0.0.0" + } + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) [String] The name of the load balancer. +* `description` - (Optional) [String] Description for the load balancer +* `method` - (Required) [String] Balancing procedure ["ROUND_ROBIN", "LEAST_CONNECTIONS"] +* `datacenter` - (Optional) [String] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `persistence` - (Optional) [Boolean] True/false defines whether persistence should be turned on/off +* `persistence_time` - (Optional) [Integer] Persistance duration in seconds +* `health_check_test` - (Optional) [String] ["TCP", "ICMP"] +* `health_check_test_interval` - (Optional) [String] +* `health_check_test_path` - (Optional) [String] +* `health_check_test_parser` - (Optional) [String] + +Loadbalancer rules (`rules`) support the following + +* `protocol` - (Required) [String] The protocol for the rule ["TCP", "UDP", "TCP/UDP", "ICMP", "IPSEC"] +* `port_balancer` - (Required) [String] +* `port_server` - (Required) [String] +* `source_ip` - (Required) [String] diff --git a/website/source/docs/providers/oneandone/r/oneandone_monitoring_policy.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_monitoring_policy.html.markdown new file mode 100644 index 000000000..2e3b6cfa2 --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_monitoring_policy.html.markdown @@ -0,0 +1,177 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_monitoring_policy" +sidebar_current: "docs-oneandone-resource-monitoring-policy" +description: |- + Creates and manages 1&1 Monitoring Policy. +--- + +# oneandone\_server + +Manages a Monitoring Policy on 1&1 + +## Example Usage + +``` +resource "oneandone_monitoring_policy" "mp" { + name = "test_mp" + agent = true + email = "jasmin@stackpointcloud.com" + + thresholds = { + cpu = { + warning = { + value = 50, + alert = false + } + critical = { + value = 66, + alert = false + } + + } + ram = { + warning = { + value = 70, + alert = true + } + critical = { + value = 80, + alert = true + } + }, + ram = { + warning = { + value = 85, + alert = true + } + critical = { + value = 95, + alert = true + } + }, + disk = { + warning = { + value = 84, + alert = true + } + critical = { + value = 94, + alert = true + } + }, + transfer = { + warning = { + value = 1000, + alert = true + } + critical = { + value = 2000, + alert = true + } + }, + internal_ping = { + warning = { + value = 3000, + alert = true + } + critical = { + value = 4000, + alert = true + } + } + } + ports = [ + { + email_notification = true + port = 443 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + }, + { + email_notification = false + port = 80 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + }, + { + email_notification = true + port = 21 + protocol = "TCP" + alert_if = "NOT_RESPONDING" + } + ] + + processes = [ + { + email_notification = false + process = "httpdeamon" + alert_if = "RUNNING" + }, + { + process = "iexplorer", + alert_if = "NOT_RUNNING" + email_notification = true + }] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) [string] The name of the VPN. +* `description` - (Optional) [string] Description for the VPN +* `email` - (Optional) [String] Email address to which notifications monitoring system will send +* `agent- (Required)[Boolean] Indicates which monitoring type will be used. True: To use this monitoring type, you must install an agent on the server. False: Monitor a server without installing an agent. Note: If you do not install an agent, you cannot retrieve information such as free hard disk space or ongoing processes. + +Monitoring Policy Thresholds (`thresholds`) support the following: + +* `cpu - (Required)[Type] CPU thresholds + * `warning - (Required)[Type] Warning alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + * `critical - (Required)[Type] Critical alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. +* `ram - (Required)[Type] RAM threshold + * `warning - (Required)[Type] Warning alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + * `critical - (Required)[Type] Critical alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. +* `disk - (Required)[Type] Hard Disk threshold + * `warning - (Required)[Type] Warning alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + * `critical - (Required)[Type] Critical alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. +* `transfer - (Required)[Type] Data transfer threshold + * `warning - (Required)[Type] Warning alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + * `critical - (Required)[Type] Critical alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. +* `internal_ping - (Required)[type] Ping threshold + * `warning - (Required)[Type] Warning alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + * `critical - (Required)[Type] Critical alert + * `value - (Required)[Integer] Warning to be issued when the threshold is reached. from 1 to 100 + * `alert - (Required)[Boolean] If set true warning will be issued. + +Monitoring Policy Ports (`ports`) support the following: + +* `email_notification - (Required)[boolean] If set true email will be sent. +* `port - (Required)[Integer] Port number. +* `protocol - (Required)[String] The protocol of the port ["TCP", "UDP", "TCP/UDP", "ICMP", "IPSEC"] +* `alert_if - (Required)[String] Condition for the alert to be issued. + +Monitoring Policy Ports (`processes`) support the following: + +* `email_notification - (Required)[Boolean] If set true email will be sent. +* `process - (Required)[Integer] Process name. +* `alert_if - (Required)[String] Condition for the alert to be issued. diff --git a/website/source/docs/providers/oneandone/r/oneandone_private_network.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_private_network.html.markdown new file mode 100644 index 000000000..0a0456127 --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_private_network.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_private_network" +sidebar_current: "docs-oneandone-resource-private-network" +description: |- + Creates and manages 1&1 Private Network. +--- + +# oneandone\_server + +Manages a Private Network on 1&1 + +## Example Usage + +``` +resource "oneandone_private_network" "pn" { + name = "pn_test", + description = "new stuff001" + datacenter = "GB" + network_address = "192.168.7.0" + subnet_mask = "255.255.255.0" + server_ids = [ + "${oneandone_server.server.id}", + "${oneandone_server.server02.id}", + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional)[string] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `description` - (Optional)[string] Description for the shared storage +* `name` - (Required)[string] The name of the private network +* `network_address` - (Optional)[string] Network address for the private network +* `subnet_mask` - (Optional)[string] Subnet mask for the private network +* `server_ids` (Optional)[Collection] List of servers that are to be associated with the private network diff --git a/website/source/docs/providers/oneandone/r/oneandone_public_ip.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_public_ip.html.markdown new file mode 100644 index 000000000..cb00d222a --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_public_ip.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_public_ip" +sidebar_current: "docs-oneandone-resource-public-ip" +description: |- + Creates and manages 1&1 Public IP. +--- + +# oneandone\_vpn + +Manages a Public IP on 1&1 + +## Example Usage + +``` +resource "oneandone_vpn" "vpn" { + datacenter = "GB" + name = "test_vpn_01" + description = "ttest descr" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional)[string] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `description` - (Optional)[string] Description of the VPN +* `name` -(Required)[string] The name of the VPN. diff --git a/website/source/docs/providers/oneandone/r/oneandone_server.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_server.html.markdown new file mode 100644 index 000000000..7e88b972a --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_server.html.markdown @@ -0,0 +1,60 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_server" +sidebar_current: "docs-oneandone-resource-server" +description: |- + Creates and manages 1&1 Server. +--- + +# oneandone\_server + +Manages a Server on 1&1 + +## Example Usage + +``` +resource "oneandone_server" "server" { + name = "Example" + description = "Terraform 1and1 tutorial" + image = "ubuntu" + datacenter = "GB" + vcores = 1 + cores_per_processor = 1 + ram = 2 + ssh_key_path = "/path/to/prvate/ssh_key" + hdds = [ + { + disk_size = 60 + is_main = true + } + ] + + provisioner "remote-exec" { + inline = [ + "apt-get update", + "apt-get -y install nginx", + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cores_per_processor` -(Required)[integer] Number of cores per processor +* `datacenter` - (Optional)[string] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `description` - (Optional)[string] Description of the server +* `firewall_policy_id` - (Optional)[string] ID of firewall policy +* `hdds` - (Required)[collection] List of HDDs. One HDD must be main. +* `*disk_size` -(Required)[integer] The size of HDD +* `*is_main` - (Optional)[boolean] Indicates if HDD is to be used as main hard disk of the server +* `image` -(Required)[string] The name of a desired image to be provisioned with the server +* `ip` - (Optional)[string] IP address for the server +* `loadbalancer_id` - (Optional)[string] ID of the load balancer +* `monitoring_policy_id` - (Optional)[string] ID of monitoring policy +* `name` -(Required)[string] The name of the server. +* `password` - (Optional)[string] Desired password. +* `ram` -(Required)[float] Size of ram. +* `ssh_key_path` - (Optional)[string] Path to private ssh key +* `vcores` -(Required)[integer] Number of virtual cores. diff --git a/website/source/docs/providers/oneandone/r/oneandone_shared_storage.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_shared_storage.html.markdown new file mode 100644 index 000000000..a8fe8224f --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_shared_storage.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_shared_storage" +sidebar_current: "docs-oneandone-resource-shared-storage" +description: |- + Creates and manages 1&1 Shared Storage. +--- + +# oneandone\_server + +Manages a Shared Storage on 1&1 + +## Example Usage + +``` +resource "oneandone_shared_storage" "storage" { + name = "test_storage1" + description = "1234" + size = 50 + + storage_servers = [ + { + id = "${oneandone_server.server.id}" + rights = "RW" + }, + { + id = "${oneandone_server.server02.id}" + rights = "RW" + } + ] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional)[string] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `description` - (Optional)[string] Description for the shared storage +* `size` - (Required)[string] Size of the shared storage +* `storage_servers` (Optional)[Collection] List of servers that will have access to the stored storage + * `id` - (Required) [string] ID of the server + * `rights` - (Required)[string] Access rights to be assigned to the server ["RW","R"] diff --git a/website/source/docs/providers/oneandone/r/oneandone_vpn.html.markdown b/website/source/docs/providers/oneandone/r/oneandone_vpn.html.markdown new file mode 100644 index 000000000..4b79e0ca7 --- /dev/null +++ b/website/source/docs/providers/oneandone/r/oneandone_vpn.html.markdown @@ -0,0 +1,30 @@ +--- +layout: "oneandone" +page_title: "1&1: oneandone_vpn" +sidebar_current: "docs-oneandone-resource-vpn" +description: |- + Creates and manages 1&1 VPN. +--- + +# oneandone\_vpn + +Manages a VPN on 1&1 + +## Example Usage + +``` +resource "oneandone_public_ip" "ip" { + "ip_type" = "IPV4" + "reverse_dns" = "test.1and1.com" + "datacenter" = "GB" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `datacenter` - (Optional)[string] Location of desired 1and1 datacenter ["DE", "GB", "US", "ES" ] +* `ip_type` - (Required)[string] IPV4 or IPV6 +* `reverese_dns` - [Optional](string) +