mirror of https://github.com/kubernetes/kops.git
openstack designate dns provider
This commit is contained in:
parent
9573ed45c0
commit
83365a992a
|
@ -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",
|
||||||
|
],
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue