diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/BUILD.bazel b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/BUILD.bazel new file mode 100644 index 0000000000..7bd61e499f --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "designate.go", + "interface.go", + "rrchangeset.go", + "rrset.go", + "rrsets.go", + "zone.go", + "zones.go", + ], + importpath = "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/openstack/designate", + visibility = ["//visibility:public"], + deps = [ + "//dnsprovider/pkg/dnsprovider:go_default_library", + "//dnsprovider/pkg/dnsprovider/rrstype:go_default_library", + "//util/pkg/vfs:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/gophercloud/gophercloud:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library", + ], +) diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/designate.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/designate.go new file mode 100644 index 0000000000..517be9bd28 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/designate.go @@ -0,0 +1,67 @@ +package designate + +import ( + "crypto/tls" + "fmt" + "io" + "net/http" + + "github.com/golang/glog" + "github.com/gophercloud/gophercloud/openstack" + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + "k8s.io/kops/util/pkg/vfs" +) + +const ( + // ProviderName is the name of this DNS provider + ProviderName = "openstack-designate" +) + +func init() { + dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) { + return newDesignate(config) + }) +} + +func newDesignate(_ io.Reader) (*Interface, error) { + oc := vfs.OpenstackConfig{} + ao, err := oc.GetCredential() + if err != nil { + return nil, err + } + + /* + pc, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, fmt.Errorf("error building openstack authenticated client: %v", err) + }*/ + + provider, err := openstack.NewClient(ao.IdentityEndpoint) + if err != nil { + return nil, fmt.Errorf("error building openstack provider client: %v", err) + } + + tlsconfig := &tls.Config{} + tlsconfig.InsecureSkipVerify = true + transport := &http.Transport{TLSClientConfig: tlsconfig} + provider.HTTPClient = http.Client{ + Transport: transport, + } + + glog.V(2).Info("authenticating to keystone") + + err = openstack.Authenticate(provider, ao) + if err != nil { + return nil, fmt.Errorf("error building openstack authenticated client: %v", err) + } + + endpointOpt, err := oc.GetServiceConfig("Designate") + if err != nil { + return nil, err + } + sc, err := openstack.NewDNSV2(provider, endpointOpt) + if err != nil { + + } + return New(sc), nil +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/interface.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/interface.go new file mode 100644 index 0000000000..c958f0b988 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/interface.go @@ -0,0 +1,22 @@ +package designate + +import ( + "github.com/gophercloud/gophercloud" + "k8s.io/kops/dnsprovider/pkg/dnsprovider" +) + +var _ dnsprovider.Interface = Interface{} + +type Interface struct { + sc *gophercloud.ServiceClient +} + +// New builds an Interface, with a specified Designate implementation. +// This is useful for testing purposes, but also if we want an instance with custom OpenStack options. +func New(sc *gophercloud.ServiceClient) *Interface { + return &Interface{sc} +} + +func (i Interface) Zones() (zones dnsprovider.Zones, supported bool) { + return Zones{&i}, true +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrchangeset.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrchangeset.go new file mode 100644 index 0000000000..13c5c4f53f --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrchangeset.go @@ -0,0 +1,136 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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. +*/ + +package designate + +import ( + "fmt" + + "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" + + "k8s.io/kops/dnsprovider/pkg/dnsprovider" +) + +var _ dnsprovider.ResourceRecordChangeset = &ResourceRecordChangeset{} + +type ResourceRecordChangeset struct { + zone *Zone + rrsets *ResourceRecordSets + + additions []dnsprovider.ResourceRecordSet + removals []dnsprovider.ResourceRecordSet + upserts []dnsprovider.ResourceRecordSet +} + +func (c *ResourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + c.additions = append(c.additions, rrset) + return c +} + +func (c *ResourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + c.removals = append(c.removals, rrset) + return c +} + +func (c *ResourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + c.upserts = append(c.upserts, rrset) + return c +} + +func (c *ResourceRecordChangeset) Apply() error { + zoneID := c.zone.impl.ID + + for _, removal := range c.removals { + rrID, err := c.nameToID(removal.Name()) + if err != nil { + + } + err = recordsets.Delete(c.zone.zones.iface.sc, zoneID, rrID).ExtractErr() + if err != nil { + + } + } + + for _, addition := range c.additions { + opts := recordsets.CreateOpts{ + Name: addition.Name(), + TTL: int(addition.Ttl()), + Type: string(addition.Type()), + Records: addition.Rrdatas(), + } + _, err := recordsets.Create(c.zone.zones.iface.sc, zoneID, opts).Extract() + if err != nil { + + } + } + + for _, upsert := range c.upserts { + rrID, err := c.nameToID(upsert.Name()) + if err != nil { + + } + uopts := recordsets.UpdateOpts{ + TTL: int(upsert.Ttl()), + Records: upsert.Rrdatas(), + } + _, err = recordsets.Update(c.zone.zones.iface.sc, zoneID, rrID, uopts).Extract() + if err != nil { + copts := recordsets.CreateOpts{ + Name: upsert.Name(), + TTL: int(upsert.Ttl()), + Type: string(upsert.Type()), + Records: upsert.Rrdatas(), + } + _, err := recordsets.Create(c.zone.zones.iface.sc, zoneID, copts).Extract() + if err != nil { + + } + } + } + + return nil +} + +func (c *ResourceRecordChangeset) IsEmpty() bool { + return len(c.removals) == 0 && len(c.additions) == 0 && len(c.upserts) == 0 +} + +// ResourceRecordSets returns the parent ResourceRecordSets +func (c *ResourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets { + return c.rrsets +} + +func (c *ResourceRecordChangeset) nameToID(name string) (string, error) { + opts := recordsets.ListOpts{ + Name: name, + } + allPages, err := recordsets.ListByZone(c.zone.zones.iface.sc, c.zone.impl.ID, opts).AllPages() + if err != nil { + + } + rrs, err := recordsets.ExtractRecordSets(allPages) + if err != nil { + + } + switch len(rrs) { + case 0: + return "", fmt.Errorf("couldn't find recordset with name: %s, expected 1", name) + case 1: + return rrs[0].ID, nil + default: + return "", fmt.Errorf("found multiple recordsets with name: %s, expected 1", name) + } +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrset.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrset.go new file mode 100644 index 0000000000..dd46091f35 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrset.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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. +*/ + +package designate + +import ( + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + "k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype" + + "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" +) + +var _ dnsprovider.ResourceRecordSet = ResourceRecordSet{} + +type ResourceRecordSet struct { + impl *recordsets.RecordSet + rrsets *ResourceRecordSets +} + +func (rrset ResourceRecordSet) Name() string { + return rrset.impl.Name +} + +func (rrset ResourceRecordSet) Rrdatas() []string { + return rrset.impl.Records +} + +func (rrset ResourceRecordSet) Ttl() int64 { + return int64(rrset.impl.TTL) +} + +func (rrset ResourceRecordSet) Type() rrstype.RrsType { + return rrstype.RrsType(rrset.impl.Type) +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrsets.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrsets.go new file mode 100644 index 0000000000..251eaf4332 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/rrsets.go @@ -0,0 +1,99 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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. +*/ + +package designate + +import ( + "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" + "github.com/gophercloud/gophercloud/pagination" + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + "k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype" +) + +var _ dnsprovider.ResourceRecordSets = ResourceRecordSets{} + +type ResourceRecordSets struct { + zone *Zone +} + +func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) { + var list []dnsprovider.ResourceRecordSet + err := recordsets.ListByZone(rrsets.zone.zones.iface.sc, rrsets.zone.impl.ID, nil).EachPage(func(page pagination.Page) (bool, error) { + rrs, err := recordsets.ExtractRecordSets(page) + if err != nil { + return false, err + } + for _, rr := range rrs { + list = append(list, &ResourceRecordSet{&rr, &rrsets}) + } + return true, nil + }) + + return list, err +} + +func (rrsets ResourceRecordSets) Get(name string) ([]dnsprovider.ResourceRecordSet, error) { + // This list implementation is very similar to the one implemented in + // the List() method above, but it restricts the retrieved list to + // the records whose name match the given `name`. + opts := &recordsets.ListOpts{ + Name: name, + } + + var list []dnsprovider.ResourceRecordSet + err := recordsets.ListByZone(rrsets.zone.zones.iface.sc, rrsets.zone.impl.ID, opts).EachPage(func(page pagination.Page) (bool, error) { + rrs, err := recordsets.ExtractRecordSets(page) + if err != nil { + return false, err + } + for _, rr := range rrs { + list = append(list, &ResourceRecordSet{&rr, &rrsets}) + } + return true, nil + }) + if err != nil { + return nil, err + } + return list, nil +} + +func (rrsets ResourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset { + return &ResourceRecordChangeset{ + zone: rrsets.zone, + rrsets: &rrsets, + } +} + +func (rrsets ResourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet { + rrs := &recordsets.RecordSet{ + Name: name, + Type: string(rrstype), + TTL: int(ttl), + } + for _, rrdata := range rrdatas { + rrs.Records = append(rrs.Records, string(rrdata)) + } + + return ResourceRecordSet{ + rrs, + &rrsets, + } +} + +// Zone returns the parent zone +func (rrset ResourceRecordSets) Zone() dnsprovider.Zone { + return rrset.zone +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zone.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zone.go new file mode 100644 index 0000000000..6758baba1d --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zone.go @@ -0,0 +1,42 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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. +*/ + +package designate + +import ( + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + + "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" +) + +var _ dnsprovider.Zone = &Zone{} + +type Zone struct { + impl zones.Zone + zones *Zones +} + +func (z *Zone) Name() string { + return z.impl.Name +} + +func (z *Zone) ID() string { + return z.impl.ID +} + +func (z *Zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) { + return &ResourceRecordSets{z}, true +} diff --git a/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zones.go b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zones.go new file mode 100644 index 0000000000..7f52871389 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/openstack/designate/zones.go @@ -0,0 +1,72 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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. +*/ + +package designate + +import ( + "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" + + "k8s.io/kops/dnsprovider/pkg/dnsprovider" +) + +var _ dnsprovider.Zones = Zones{} + +type Zones struct { + iface *Interface +} + +func (kzs Zones) List() ([]dnsprovider.Zone, error) { + var zoneList []dnsprovider.Zone + + allPages, err := zones.List(kzs.iface.sc, nil).AllPages() + if err != nil { + return zoneList, err + } + zs, err := zones.ExtractZones(allPages) + if err != nil { + return zoneList, err + } + for _, z := range zs { + kz := &Zone{ + impl: z, + zones: &kzs, + } + zoneList = append(zoneList, kz) + } + return zoneList, nil +} + +func (kzs Zones) Add(zone dnsprovider.Zone) (dnsprovider.Zone, error) { + opts := &zones.CreateOpts{Name: zone.Name()} + z, err := zones.Create(kzs.iface.sc, opts).Extract() + if err != nil { + return nil, err + } + return &Zone{ + impl: *z, + zones: &kzs, + }, nil +} + +func (kzs Zones) Remove(zone dnsprovider.Zone) error { + _, err := zones.Delete(kzs.iface.sc, zone.ID()).Extract() + return err +} + +func (kzs Zones) New(name string) (dnsprovider.Zone, error) { + zone := zones.Zone{Name: name} + return &Zone{zone, &kzs}, nil +}