diff --git a/anxcloud/provider.go b/anxcloud/provider.go index c4741512..b1cd4288 100644 --- a/anxcloud/provider.go +++ b/anxcloud/provider.go @@ -24,6 +24,7 @@ func Provider() *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ "anxcloud_virtual_server": resourceVirtualServer(), + "anxcloud_vlan": resourceVLAN(), }, DataSourcesMap: map[string]*schema.Resource{}, ConfigureContextFunc: providerConfigure, diff --git a/anxcloud/resource_virtual_server.go b/anxcloud/resource_virtual_server.go index 406d6e1f..0508b50e 100644 --- a/anxcloud/resource_virtual_server.go +++ b/anxcloud/resource_virtual_server.go @@ -201,12 +201,12 @@ func resourceVirtualServerDelete(ctx context.Context, d *schema.ResourceData, m _, err := v.Info().Get(ctx, d.Id()) if err != nil { if err := handleNotFoundError(err); err != nil { - return resource.NonRetryableError(fmt.Errorf("unable to get vm '%s': %w", d.Id(), err)) + return resource.NonRetryableError(fmt.Errorf("unable to get vm with id '%s': %w", d.Id(), err)) } d.SetId("") return nil } - return resource.RetryableError(fmt.Errorf("waiting for vm with ID '%s' to be deleted", d.Id())) + return resource.RetryableError(fmt.Errorf("waiting for vm with id '%s' to be deleted", d.Id())) }) if err != nil { return diag.FromErr(err) diff --git a/anxcloud/resource_virtual_server_test.go b/anxcloud/resource_virtual_server_test.go index f4804d90..ddf88b74 100644 --- a/anxcloud/resource_virtual_server_test.go +++ b/anxcloud/resource_virtual_server_test.go @@ -19,7 +19,7 @@ func TestAccAnxcloudVirtualServerBasic(t *testing.T) { locationID := "52b5f6b2fd3a4a7eaaedf1a7c019e9ea" templateID := "12c28aa7-604d-47e9-83fb-5f1d1f1837b3" - vlanID := "ff70791b398e4ab29786dd34f211694c" + vlanID := "02f39d20ca0f4adfb5032f88dbc26c39" cpus := 4 memory := 4096 diskSize := 50 @@ -66,7 +66,7 @@ func testAccCheckAnxcloudVirtualServerDestroy(s *terraform.State) error { return nil } if info.Identifier != "" { - return fmt.Errorf("virtual machine '%s' exist", info.Identifier) + return fmt.Errorf("virtual machine '%s' exists", info.Identifier) } } @@ -102,11 +102,11 @@ func testAccCheckAnxcloudVirtualServerExists(n string) resource.TestCheckFunc { ctx := context.Background() if !ok { - return fmt.Errorf("not found: %s", n) + return fmt.Errorf("virtual server not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("id not set for virtual machine") + return fmt.Errorf("virtual server id not set") } info, err := v.Info().Get(ctx, rs.Primary.ID) @@ -115,7 +115,7 @@ func testAccCheckAnxcloudVirtualServerExists(n string) resource.TestCheckFunc { } if info.Status != vmPoweredOn { - return fmt.Errorf("virtual machine found but it is in '%s' state", info.Status) + return fmt.Errorf("virtual machine found but it is not in the expected state '%s': '%s'", vmPoweredOn, info.Status) } return nil diff --git a/anxcloud/resource_vlan.go b/anxcloud/resource_vlan.go new file mode 100644 index 00000000..47947d75 --- /dev/null +++ b/anxcloud/resource_vlan.go @@ -0,0 +1,147 @@ +package anxcloud + +import ( + "context" + "fmt" + "time" + + "github.com/anexia-it/go-anxcloud/pkg/client" + "github.com/anexia-it/go-anxcloud/pkg/vlan" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + vlanStatusActive = "Active" +) + +func resourceVLAN() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceVLANCreate, + ReadContext: resourceVLANRead, + UpdateContext: resourceVLANUpdate, + DeleteContext: resourceVLANDelete, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Read: schema.DefaultTimeout(1 * time.Minute), + Update: schema.DefaultTimeout(15 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + Schema: schemaVLAN(), + } +} + +func resourceVLANCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(client.Client) + v := vlan.NewAPI(c) + locationID := d.Get("location_id").(string) + + def := vlan.CreateDefinition{ + Location: locationID, + VMProvisioning: d.Get("vm_provisioning").(bool), + CustomerDescription: d.Get("description_customer").(string), + } + res, err := v.Create(ctx, def) + if err != nil { + return diag.FromErr(err) + } + d.SetId(res.Identifier) + + err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + vlan, err := v.Get(ctx, d.Id()) + if err != nil { + return resource.NonRetryableError(fmt.Errorf("unable to fetch vlan with '%s' id: %w", d.Id(), err)) + } + if vlan.Status == vlanStatusActive { + return nil + } + return resource.RetryableError(fmt.Errorf("waiting for vlan with '%s' id to be '%s'", d.Id(), vlanStatusActive)) + }) + if err != nil { + return diag.FromErr(err) + } + + return resourceVLANRead(ctx, d, m) +} + +func resourceVLANRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags []diag.Diagnostic + c := m.(client.Client) + v := vlan.NewAPI(c) + + vlan, err := v.Get(ctx, d.Id()) + if err != nil { + return diag.FromErr(err) + } + + if err := d.Set("name", vlan.Name); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + if err := d.Set("role_text", vlan.Role); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + if err := d.Set("status", vlan.Status); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + if err := d.Set("description_customer", vlan.CustomerDescription); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + if err := d.Set("description_internal", vlan.InternalDescription); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + if err := d.Set("locations", flattenVLANLocations(vlan.Locations)); err != nil { + diags = append(diags, diag.FromErr(err)...) + } + + return diags +} + +func resourceVLANUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(client.Client) + v := vlan.NewAPI(c) + + if !d.HasChange("description_customer") { + return nil + } + + def := vlan.UpdateDefinition{ + CustomerDescription: d.Get("description_customer").(string), + } + if err := v.Update(ctx, d.Id(), def); err != nil { + return diag.FromErr(err) + } + + return resourceVLANRead(ctx, d, m) +} + +func resourceVLANDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(client.Client) + v := vlan.NewAPI(c) + + err := v.Delete(ctx, d.Id()) + if err != nil { + if err := handleNotFoundError(err); err != nil { + return diag.FromErr(err) + } + d.SetId("") + return nil + } + + err = resource.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *resource.RetryError { + _, err := v.Get(ctx, d.Id()) + if err != nil { + if err := handleNotFoundError(err); err != nil { + return resource.NonRetryableError(fmt.Errorf("unable to get vlan with id '%s': %w", d.Id(), err)) + } + d.SetId("") + return nil + } + return resource.RetryableError(fmt.Errorf("waiting for vlan with id '%s' to be deleted", d.Id())) + }) + if err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/anxcloud/resource_vlan_test.go b/anxcloud/resource_vlan_test.go new file mode 100644 index 00000000..186ea529 --- /dev/null +++ b/anxcloud/resource_vlan_test.go @@ -0,0 +1,117 @@ +package anxcloud + +import ( + "context" + "fmt" + "testing" + + "github.com/anexia-it/go-anxcloud/pkg/client" + "github.com/anexia-it/go-anxcloud/pkg/vlan" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAnxcloudVLAN(t *testing.T) { + resourceName := "acc_test_basic" + resourcePath := "anxcloud_vlan." + resourceName + + locationID := "52b5f6b2fd3a4a7eaaedf1a7c019e9ea" + customerDescription := "vlan acceptance tests" + customerDescriptionUpdate := "vlan acceptance tests update" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckAnxcloudVLANDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAnxcloudVLAN(resourceName, locationID, customerDescription), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourcePath, "location_id", locationID), + resource.TestCheckResourceAttr(resourcePath, "vm_provisioning", "true"), + resource.TestCheckResourceAttr(resourcePath, "description_customer", customerDescription), + testAccCheckAnxcloudVLANExists(resourcePath, customerDescription), + ), + }, + { + Config: testAccCheckAnxcloudVLAN(resourceName, locationID, customerDescriptionUpdate), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourcePath, "location_id", locationID), + resource.TestCheckResourceAttr(resourcePath, "vm_provisioning", "true"), + resource.TestCheckResourceAttr(resourcePath, "description_customer", customerDescriptionUpdate), + testAccCheckAnxcloudVLANExists(resourcePath, customerDescriptionUpdate), + ), + }, + }, + }) +} + +func testAccCheckAnxcloudVLANDestroy(s *terraform.State) error { + c := testAccProvider.Meta().(client.Client) + v := vlan.NewAPI(c) + ctx := context.Background() + for _, rs := range s.RootModule().Resources { + if rs.Type != "anxcloud_vlan" { + continue + } + + if rs.Primary.ID == "" { + return nil + } + + info, err := v.Get(ctx, rs.Primary.ID) + if err != nil { + if err := handleNotFoundError(err); err != nil { + return err + } + return nil + } + if info.Identifier != "" { + return fmt.Errorf("vlan '%s' exists", info.Identifier) + } + } + + return nil +} + +func testAccCheckAnxcloudVLAN(resourceName, locationID, customerDescription string) string { + return fmt.Sprintf(` + resource "anxcloud_vlan" "%s" { + location_id = "%s" + vm_provisioning = true + description_customer = "%s" + } + `, resourceName, locationID, customerDescription) +} + +func testAccCheckAnxcloudVLANExists(n string, expectedCustomerDescription string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + c := testAccProvider.Meta().(client.Client) + v := vlan.NewAPI(c) + ctx := context.Background() + + if !ok { + return fmt.Errorf("vlan not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("vlan id not set") + } + + i, err := v.Get(ctx, rs.Primary.ID) + if err != nil { + return err + } + + if i.Status != vlanStatusActive { + return fmt.Errorf("vlan found but it is not in the expected state '%s': %s", vlanStatusActive, i.Status) + } + + if i.CustomerDescription != expectedCustomerDescription { + return fmt.Errorf("customer description is different than expected '%s': '%s'", i.CustomerDescription, expectedCustomerDescription) + } + + return nil + } +} diff --git a/anxcloud/schema_vlan.go b/anxcloud/schema_vlan.go new file mode 100644 index 00000000..e892b6aa --- /dev/null +++ b/anxcloud/schema_vlan.go @@ -0,0 +1,92 @@ +package anxcloud + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func schemaVLAN() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "location_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ANX Location Identifier.", + }, + "vm_provisioning": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + Description: "True if VM provisioning shall be enabled. Defaults to false.", + }, + "description_customer": { + Type: schema.TypeString, + Optional: true, + Description: "Additional customer description.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "VLAN name.", + }, + "description_internal": { + Type: schema.TypeString, + Computed: true, + Description: "Internal description.", + }, + "role_text": { + Type: schema.TypeString, + Computed: true, + Description: "Role of the VLAN.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "VLAN status.", + }, + "locations": { + Type: schema.TypeList, + Computed: true, + Description: "Role of the VLAN.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "identifier": { + Type: schema.TypeString, + Computed: true, + Description: "Identifier of the location.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the location.", + }, + "code": { + Type: schema.TypeString, + Computed: true, + Description: "Location code.", + }, + "country": { + Type: schema.TypeString, + Computed: true, + Description: "Location country.", + }, + "city_code": { + Type: schema.TypeString, + Computed: true, + Description: "Location city code.", + }, + "lat": { + Type: schema.TypeString, + Computed: true, + Description: "Location latitude.", + }, + "lon": { + Type: schema.TypeString, + Computed: true, + Description: "Location longitude.", + }, + }, + }, + }, + } +} diff --git a/anxcloud/struct_vlan.go b/anxcloud/struct_vlan.go new file mode 100644 index 00000000..9ee99832 --- /dev/null +++ b/anxcloud/struct_vlan.go @@ -0,0 +1,32 @@ +package anxcloud + +import ( + "github.com/anexia-it/go-anxcloud/pkg/vlan" +) + +// expanders + +// flatteners + +func flattenVLANLocations(in []vlan.Location) []interface{} { + att := []interface{}{} + if len(in) < 1 { + return att + } + + for _, l := range in { + m := map[string]interface{}{} + + m["identifier"] = l.Identifier + m["name"] = l.Name + m["code"] = l.Code + m["city_code"] = l.CityCode + m["country"] = l.CountryCode + m["lat"] = l.Latitude + m["lon"] = l.Longitude + + att = append(att, m) + } + + return att +} diff --git a/anxcloud/struct_vlan_test.go b/anxcloud/struct_vlan_test.go new file mode 100644 index 00000000..a6e2f364 --- /dev/null +++ b/anxcloud/struct_vlan_test.go @@ -0,0 +1,73 @@ +package anxcloud + +import ( + "testing" + + "github.com/anexia-it/go-anxcloud/pkg/vlan" + "github.com/google/go-cmp/cmp" +) + +// expanders tests + +// flatteners tests + +func TestFlattenVLANLocation(t *testing.T) { + cases := []struct { + Input []vlan.Location + ExpectedOutput []interface{} + }{ + { + []vlan.Location{ + { + Identifier: "52b5f6b2fd3a4a7eaaedf1a7c0191234", + Name: "AT, Vienna, Test", + Code: "ANX-8888", + CountryCode: "AT", + Latitude: "0.0", + Longitude: "0.0", + CityCode: "VIE", + }, + { + Identifier: "72c5f6b2fd3a4a7eaaedf1a7c0194321", + Name: "AT, Vienna, Test2", + Code: "ANX-8889", + CountryCode: "AT", + Latitude: "1.1", + Longitude: "1.1", + CityCode: "VIE", + }, + }, + []interface{}{ + map[string]interface{}{ + "identifier": "52b5f6b2fd3a4a7eaaedf1a7c0191234", + "name": "AT, Vienna, Test", + "code": "ANX-8888", + "country": "AT", + "lat": "0.0", + "lon": "0.0", + "city_code": "VIE", + }, + map[string]interface{}{ + "identifier": "72c5f6b2fd3a4a7eaaedf1a7c0194321", + "name": "AT, Vienna, Test2", + "code": "ANX-8889", + "country": "AT", + "lat": "1.1", + "lon": "1.1", + "city_code": "VIE", + }, + }, + }, + { + []vlan.Location{}, + []interface{}{}, + }, + } + + for _, tc := range cases { + output := flattenVLANLocations(tc.Input) + if diff := cmp.Diff(tc.ExpectedOutput, output); diff != "" { + t.Fatalf("Unexpected output from expander: mismatch (-want +got):\n%s", diff) + } + } +} diff --git a/docs/development.md b/docs/development.md index 3173444d..092d10e1 100644 --- a/docs/development.md +++ b/docs/development.md @@ -54,4 +54,4 @@ or run specific test case: ``` make testacc TESTARGS='-run=TestAccXXX' -``` \ No newline at end of file +``` diff --git a/docs/guides/authenticating.md b/docs/guides/authenticating.md index 6863f2c8..99cdd6a1 100644 --- a/docs/guides/authenticating.md +++ b/docs/guides/authenticating.md @@ -18,4 +18,4 @@ provider "anxcloud" { } ``` -The last authentication method is highly **NOT** recommended for production environments. \ No newline at end of file +The last authentication method is highly **NOT** recommended for production environments. diff --git a/docs/resources/virtual_server.md b/docs/resources/virtual_server.md index f0e9adcf..0556659b 100644 --- a/docs/resources/virtual_server.md +++ b/docs/resources/virtual_server.md @@ -1,11 +1,11 @@ --- -page_title: "virtual_server Resource - terraform-provider-hashicups" +page_title: "virtual_server resource - terraform-provider-anxcloud" subcategory: "" description: |- The Virual Server resource allows you to create virtual machines at Anexia Cloud. --- -# Resource `hashicups_order` +# Resource `anxcloud_virtual_server` -> Visit the [Perform CRUD operations with Providers](https://learn.hashicorp.com/tutorials/terraform/provider-use?in=terraform/providers&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) Learn tutorial for an interactive getting started experience. @@ -78,8 +78,8 @@ Each order item contains a `coffee` object and a `quantity`. In addition to all the arguments above, the following attributes are exported: -`id` - Virtual Server identifier. -`info` - Virtual Server details. +- `id` - Virtual Server identifier. +- `info` - Virtual Server details. See [info](#info) below for details. ### Info diff --git a/docs/resources/vlan.md b/docs/resources/vlan.md new file mode 100644 index 00000000..f6f8fea5 --- /dev/null +++ b/docs/resources/vlan.md @@ -0,0 +1,49 @@ +--- +page_title: "vlan resource - terraform-provider-anxcloud" +subcategory: "" +description: |- + The VLAN resource allows you to create VLAN at Anexia Cloud. +--- + +# Resource `anxcloud_vlan` + +-> Visit the [Perform CRUD operations with Providers](https://learn.hashicorp.com/tutorials/terraform/provider-use?in=terraform/providers&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) Learn tutorial for an interactive getting started experience. + +The VLAN resource allows you to configure and create VLAN at Anexia Cloud. + +## Example Usage + +```hcl +resource "anxcloud_vlan" "example" { + location_id = "52b5f6b2fd3a4a7eaaedf1a7c019e9ea" + vm_provisioning = true + description_customer = "sample vlan" +} +``` + +## Argument Reference + +- `location_id` - (Required) Location identifier. +- `vm_provisioning` - (Optional) True if VM provisioning shall be enabled. Defaults to false. +- `description_customer` - (Optional) Additional customer description. + +## Attributes Reference + +In addition to all the arguments above, the following attributes are exported: + +- `id` - VLAN identifier. +- `name` - Generated VLAN name. +- `description_internal` - Internal description of the VLAN. +- `role_text` - Role of the VLAN in text format. +- `status` - VLAN status. +- `locations` - VLAN locations. See [locations](#locations) below for details. + +### Locations + +- `identifier` - Location identifier. +- `name` - Location name. +- `code` - Location code. +- `country` - Location country. +- `city_code` - Location city code. +- `lat` - Location latitude. +- `lon` - Location longitude. diff --git a/examples/main.tf b/examples/main.tf index e19513f3..bce1f478 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -32,3 +32,9 @@ resource "anxcloud_virtual_server" "example" { dns = ["8.8.8.8"] } + +resource "anxcloud_vlan" "example" { + location_id = "52b5f6b2fd3a4a7eaaedf1a7c019e9ea" + vm_provisioning = true + description_customer = "sample vlan" +} diff --git a/go.mod b/go.mod index 30390ceb..de639ae8 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module github.com/anexia-it/terraform-provider-anxcloud go 1.14 require ( - github.com/anexia-it/go-anxcloud v0.3.1 + github.com/anexia-it/go-anxcloud v0.3.2 github.com/fatih/color v1.10.0 // indirect + github.com/go-test/deep v1.0.7 // indirect github.com/google/go-cmp v0.5.2 github.com/google/uuid v1.1.2 // indirect github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 @@ -12,10 +13,12 @@ require ( github.com/hashicorp/go-multierror v1.1.0 // indirect github.com/hashicorp/hcl/v2 v2.6.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.2.0 + github.com/imdario/mergo v0.3.11 // indirect github.com/lithammer/shortuuid v3.0.0+incompatible github.com/stretchr/testify v1.6.1 // indirect github.com/zclconf/go-cty v1.6.1 // indirect golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 // indirect ) diff --git a/go.sum b/go.sum index 53797a8c..15835746 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/anexia-it/go-anxcloud v0.3.1 h1:Dv9YIstw6d5RN4YP+jAIApcELUXfoC8dxJq6LxWy62U= -github.com/anexia-it/go-anxcloud v0.3.1/go.mod h1:cevqezsbOJ4GBlAWaztfLKl9w4VzxJBt4ipgHORi3gw= +github.com/anexia-it/go-anxcloud v0.3.2 h1:XXWLGKWtAoXwJaClx76EQPq+0oRAQzt1FdqtdUbQ7cY= +github.com/anexia-it/go-anxcloud v0.3.2/go.mod h1:cevqezsbOJ4GBlAWaztfLKl9w4VzxJBt4ipgHORi3gw= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= @@ -104,6 +104,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= +github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -213,6 +215,8 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= @@ -590,8 +594,12 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 h1:OfFoIUYv/me30yv7XlMy4F9RJw8DEm8WQ6QG1Ph4bH0= +gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=