Provider Oneandone (#13633)

* Terraform Provider 1&1

* Addressing pull request remarks

* Fixed imports

* Fixing remarks

* Test optimiziation
This commit is contained in:
Jasmin Gacic 2017-04-21 16:19:10 +02:00 committed by Paul Stack
parent 525a21a104
commit 61499cfcf0
64 changed files with 11355 additions and 9 deletions

View File

@ -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 package local

View File

@ -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 package backend

View File

@ -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
}

View File

@ -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()
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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"
},
]
}`

View File

@ -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
}

View File

@ -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"
}
]
}`

View File

@ -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
}

View File

@ -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
}]
}`

View File

@ -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
}

View File

@ -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}"
]
}
`

View File

@ -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
}

View File

@ -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"
}`

View File

@ -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
}

View File

@ -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
}
]
}`

View File

@ -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
}

View File

@ -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"
}`

View File

@ -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
}

View File

@ -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"
}`

View File

@ -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 package command

View File

@ -47,6 +47,7 @@ import (
nomadprovider "github.com/hashicorp/terraform/builtin/providers/nomad" nomadprovider "github.com/hashicorp/terraform/builtin/providers/nomad"
ns1provider "github.com/hashicorp/terraform/builtin/providers/ns1" ns1provider "github.com/hashicorp/terraform/builtin/providers/ns1"
nullprovider "github.com/hashicorp/terraform/builtin/providers/null" nullprovider "github.com/hashicorp/terraform/builtin/providers/null"
oneandoneprovider "github.com/hashicorp/terraform/builtin/providers/oneandone"
opcprovider "github.com/hashicorp/terraform/builtin/providers/opc" opcprovider "github.com/hashicorp/terraform/builtin/providers/opc"
openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack" openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack"
opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie" opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie"
@ -126,6 +127,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
"ns1": ns1provider.Provider, "ns1": ns1provider.Provider,
"null": nullprovider.Provider, "null": nullprovider.Provider,
"opc": opcprovider.Provider, "opc": opcprovider.Provider,
"oneandone": oneandoneprovider.Provider,
"openstack": openstackprovider.Provider, "openstack": openstackprovider.Provider,
"opsgenie": opsgenieprovider.Provider, "opsgenie": opsgenieprovider.Provider,
"packet": packetprovider.Provider, "packet": packetprovider.Provider,

View File

@ -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 package config

View File

@ -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 package schema

View File

@ -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 package schema

View File

@ -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 package terraform

View File

@ -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 package terraform

View File

@ -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 package terraform

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

6
vendor/vendor.json vendored
View File

@ -14,6 +14,12 @@
"revision": "81b7822b1e798e8f17bf64b59512a5be4097e966", "revision": "81b7822b1e798e8f17bf64b59512a5be4097e966",
"revisionTime": "2017-01-18T16:13:56Z" "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=", "checksumSHA1": "N92Zji40JkCHAnsCNHTP4iKPz88=",
"comment": "v2.1.1-beta-8-gca4d906", "comment": "v2.1.1-beta-8-gca4d906",

View File

@ -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.

View File

@ -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

View File

@ -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]

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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"]

View File

@ -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)