Switch to github.com/golang/dep for vendoring

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-01-31 16:45:59 -08:00
parent d6ab91be27
commit 8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions

View file

@ -0,0 +1,51 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dns.go",
"doc.go",
"plugins.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//vendor:github.com/golang/glog",
],
)
go_test(
name = "go_default_test",
srcs = ["dns_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//federation/pkg/dnsprovider/rrstype:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//federation/pkg/dnsprovider/providers/aws/route53:all-srcs",
"//federation/pkg/dnsprovider/providers/coredns:all-srcs",
"//federation/pkg/dnsprovider/providers/google/clouddns:all-srcs",
"//federation/pkg/dnsprovider/rrstype:all-srcs",
"//federation/pkg/dnsprovider/tests:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,100 @@
/*
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 dnsprovider
import (
"reflect"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Interface is an abstract, pluggable interface for DNS providers.
type Interface interface {
// Zones returns the provider's Zones interface, or false if not supported.
Zones() (Zones, bool)
}
type Zones interface {
// List returns the managed Zones, or an error if the list operation failed.
List() ([]Zone, error)
// Add creates and returns a new managed zone, or an error if the operation failed
Add(Zone) (Zone, error)
// Remove deletes a managed zone, or returns an error if the operation failed.
Remove(Zone) error
// New allocates a new Zone, which can then be passed to Add()
// Arguments are as per the Zone interface below.
New(name string) (Zone, error)
}
type Zone interface {
// Name returns the name of the zone, e.g. "example.com"
Name() string
// ID returns the unique provider identifier for the zone
ID() string
// ResourceRecordsets returns the provider's ResourceRecordSets interface, or false if not supported.
ResourceRecordSets() (ResourceRecordSets, bool)
}
type ResourceRecordSets interface {
// List returns the ResourceRecordSets of the Zone, or an error if the list operation failed.
List() ([]ResourceRecordSet, error)
// Get returns the ResourceRecordSet with the name in the Zone. if the named resource record set does not exist, but no error occurred, the returned set, and error, are both nil.
Get(name string) (ResourceRecordSet, error)
// New allocates a new ResourceRecordSet, which can then be passed to ResourceRecordChangeset Add() or Remove()
// Arguments are as per the ResourceRecordSet interface below.
New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) ResourceRecordSet
// StartChangeset begins a new batch operation of changes against the Zone
StartChangeset() ResourceRecordChangeset
}
// ResourceRecordChangeset accumulates a set of changes, that can then be applied with Apply
type ResourceRecordChangeset interface {
// Add adds the creation of a ResourceRecordSet in the Zone to the changeset
Add(ResourceRecordSet) ResourceRecordChangeset
// Remove adds the removal of a ResourceRecordSet in the Zone to the changeset
// The supplied ResourceRecordSet must match one of the existing recordsets (obtained via List()) exactly.
Remove(ResourceRecordSet) ResourceRecordChangeset
// Apply applies the accumulated operations to the Zone.
Apply() error
}
type ResourceRecordSet interface {
// Name returns the name of the ResourceRecordSet, e.g. "www.example.com".
Name() string
// Rrdatas returns the Resource Record Datas of the record set.
Rrdatas() []string
// Ttl returns the time-to-live of the record set, in seconds.
Ttl() int64
// Type returns the type of the record set (A, CNAME, SRV, etc)
Type() rrstype.RrsType
}
/* ResourceRecordSetsEquivalent compares two ResourceRecordSets for semantic equivalence.
Go's equality operator doesn't work the way we want it to in this case,
hence the need for this function.
More specifically (from the Go spec):
"Two struct values are equal if their corresponding non-blank fields are equal."
In our case, there may be some private internal member variables that may not be not equal,
but we want the two structs to be considered equivalent anyway, if the fields exposed
via their interfaces are equal.
*/
func ResourceRecordSetsEquivalent(r1, r2 ResourceRecordSet) bool {
if r1.Name() == r2.Name() && reflect.DeepEqual(r1.Rrdatas(), r2.Rrdatas()) && r1.Ttl() == r2.Ttl() && r1.Type() == r2.Type() {
return true
}
return false
}

View file

@ -0,0 +1,96 @@
/*
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 dnsprovider
import (
"testing"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time interface check
var _ ResourceRecordSet = record{}
type record struct {
name string
rrdatas []string
ttl int64
type_ string
}
func (r record) Name() string {
return r.name
}
func (r record) Ttl() int64 {
return r.ttl
}
func (r record) Rrdatas() []string {
return r.rrdatas
}
func (r record) Type() rrstype.RrsType {
return rrstype.RrsType(r.type_)
}
const testDNSZone string = "foo.com"
var testData = []struct {
inputs [2]record
expectedOutput bool
}{
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}}, true,
},
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical except Name
{"bar", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}}, false,
},
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical except Rrdata
{"foo", []string{"1.2.3.4", "5,6,7,9"}, 180, "A"}}, false,
},
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical except Rrdata ordering reversed
{"foo", []string{"5,6,7,8", "1.2.3.4"}, 180, "A"}}, false,
},
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical except TTL
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 150, "A"}}, false,
},
{
[2]record{
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "A"}, // Identical except Type
{"foo", []string{"1.2.3.4", "5,6,7,8"}, 180, "CNAME"}}, false,
},
}
func TestEquivalent(t *testing.T) {
for _, test := range testData {
output := ResourceRecordSetsEquivalent(test.inputs[0], test.inputs[1])
if output != test.expectedOutput {
t.Errorf("Expected equivalence comparison of %q and %q to yield %v, but it vielded %v", test.inputs[0], test.inputs[1], test.expectedOutput, output)
}
}
}

View file

@ -0,0 +1,21 @@
/*
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.
*/
/*
dnsprovider supplies interfaces for dns service providers (e.g. Google Cloud DNS, AWS route53, etc).
Implementations exist in the providers sub-package
*/
package dnsprovider // import "k8s.io/kubernetes/federation/pkg/dnsprovider"

View file

@ -0,0 +1,109 @@
/*
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 dnsprovider
import (
"fmt"
"io"
"os"
"sync"
"github.com/golang/glog"
)
// Factory is a function that returns a dnsprovider.Interface.
// The config parameter provides an io.Reader handler to the factory in
// order to load specific configurations. If no configuration is provided
// the parameter is nil.
type Factory func(config io.Reader) (Interface, error)
// All registered dns providers.
var providersMutex sync.Mutex
var providers = make(map[string]Factory)
// RegisterDnsProvider registers a dnsprovider.Factory by name. This
// is expected to happen during startup.
func RegisterDnsProvider(name string, cloud Factory) {
providersMutex.Lock()
defer providersMutex.Unlock()
if _, found := providers[name]; found {
glog.Fatalf("DNS provider %q was registered twice", name)
}
glog.V(1).Infof("Registered DNS provider %q", name)
providers[name] = cloud
}
// GetDnsProvider creates an instance of the named DNS provider, or nil if
// the name is not known. The error return is only used if the named provider
// was known but failed to initialize. The config parameter specifies the
// io.Reader handler of the configuration file for the DNS provider, or nil
// for no configuration.
func GetDnsProvider(name string, config io.Reader) (Interface, error) {
providersMutex.Lock()
defer providersMutex.Unlock()
f, found := providers[name]
if !found {
return nil, nil
}
return f(config)
}
// Returns a list of registered dns providers.
func RegisteredDnsProviders() []string {
registeredProviders := make([]string, len(providers))
i := 0
for provider := range providers {
registeredProviders[i] = provider
i = i + 1
}
return registeredProviders
}
// InitDnsProvider creates an instance of the named DNS provider.
func InitDnsProvider(name string, configFilePath string) (Interface, error) {
var dns Interface
var err error
if name == "" {
glog.Info("No DNS provider specified.")
return nil, nil
}
if configFilePath != "" {
var config *os.File
config, err = os.Open(configFilePath)
if err != nil {
return nil, fmt.Errorf("Couldn't open DNS provider configuration %s: %#v", configFilePath, err)
}
defer config.Close()
dns, err = GetDnsProvider(name, config)
} else {
// Pass explicit nil so plugins can actually check for nil. See
// "Why is my nil error value not equal to nil?" in golang.org/doc/faq.
dns, err = GetDnsProvider(name, nil)
}
if err != nil {
return nil, fmt.Errorf("could not init DNS provider %q: %v", name, err)
}
if dns == nil {
return nil, fmt.Errorf("unknown DNS provider %q", name)
}
return dns, nil
}

View file

@ -0,0 +1,63 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"interface.go",
"route53.go",
"rrchangeset.go",
"rrset.go",
"rrsets.go",
"zone.go",
"zones.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/providers/aws/route53/stubs:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//pkg/util/uuid:go_default_library",
"//vendor:github.com/aws/aws-sdk-go/aws",
"//vendor:github.com/aws/aws-sdk-go/aws/session",
"//vendor:github.com/aws/aws-sdk-go/service/route53",
],
)
go_test(
name = "go_default_test",
srcs = ["route53_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/providers/aws/route53/stubs:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//federation/pkg/dnsprovider/tests:go_default_library",
"//vendor:github.com/aws/aws-sdk-go/aws",
"//vendor:github.com/aws/aws-sdk-go/service/route53",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//federation/pkg/dnsprovider/providers/aws/route53/stubs:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,39 @@
/*
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 route53
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/stubs"
)
// Compile time check for interface adherence
var _ dnsprovider.Interface = Interface{}
type Interface struct {
service stubs.Route53API
}
// New builds an Interface, with a specified Route53API implementation.
// This is useful for testing purposes, but also if we want an instance with with custom AWS options.
func New(service stubs.Route53API) *Interface {
return &Interface{service}
}
func (i Interface) Zones() (zones dnsprovider.Zones, supported bool) {
return Zones{&i}, true
}

View file

@ -0,0 +1,44 @@
/*
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.
*/
// route53 is the implementation of pkg/dnsprovider interface for AWS Route53
package route53
import (
"io"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)
const (
ProviderName = "aws-route53"
)
func init() {
dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) {
return newRoute53(config)
})
}
// newRoute53 creates a new instance of an AWS Route53 DNS Interface.
func newRoute53(config io.Reader) (*Interface, error) {
// Connect to AWS Route53 - TODO: Do more sophisticated auth
svc := route53.New(session.New())
return New(svc), nil
}

View file

@ -0,0 +1,295 @@
/*
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 route53
import (
"flag"
"fmt"
"os"
"testing"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
route53testing "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/stubs"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"k8s.io/kubernetes/federation/pkg/dnsprovider/tests"
)
func newTestInterface() (dnsprovider.Interface, error) {
// Use this to test the real cloud service.
// return dnsprovider.GetDnsProvider(ProviderName, strings.NewReader("\n[global]\nproject-id = federation0-cluster00"))
return newFakeInterface() // Use this to stub out the entire cloud service
}
func newFakeInterface() (dnsprovider.Interface, error) {
var service route53testing.Route53API
service = route53testing.NewRoute53APIStub()
iface := New(service)
// Add a fake zone to test against.
params := &route53.CreateHostedZoneInput{
CallerReference: aws.String("Nonce"), // Required
Name: aws.String("example.com"), // Required
}
_, err := iface.service.CreateHostedZone(params)
if err != nil {
return nil, err
}
return iface, nil
}
var interface_ dnsprovider.Interface
func TestMain(m *testing.M) {
fmt.Printf("Parsing flags.\n")
flag.Parse()
var err error
fmt.Printf("Getting new test interface.\n")
interface_, err = newTestInterface()
if err != nil {
fmt.Printf("Error creating interface: %v", err)
os.Exit(1)
}
fmt.Printf("Running tests...\n")
os.Exit(m.Run())
}
// zones returns the zones interface for the configured dns provider account/project,
// or fails if it can't be found
func zones(t *testing.T) dnsprovider.Zones {
zonesInterface, supported := interface_.Zones()
if !supported {
t.Fatalf("Zones interface not supported by interface %v", interface_)
} else {
t.Logf("Got zones %v\n", zonesInterface)
}
return zonesInterface
}
// firstZone returns the first zone for the configured dns provider account/project,
// or fails if it can't be found
func firstZone(t *testing.T) dnsprovider.Zone {
t.Logf("Getting zones")
z := zones(t)
zones, err := z.List()
if err != nil {
t.Fatalf("Failed to list zones: %v", err)
} else {
t.Logf("Got zone list: %v\n", zones)
}
if len(zones) < 1 {
t.Fatalf("Zone listing returned %d, expected >= %d", len(zones), 1)
} else {
t.Logf("Got at least 1 zone in list:%v\n", zones[0])
}
return zones[0]
}
/* rrs returns the ResourceRecordSets interface for a given zone */
func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) {
rrsets, supported := zone.ResourceRecordSets()
if !supported {
t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone)
return r
}
return rrsets
}
func listRrsOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets) []dnsprovider.ResourceRecordSet {
rrset, err := rrsets.List()
if err != nil {
t.Fatalf("Failed to list recordsets: %v", err)
} else {
if len(rrset) < 0 {
t.Fatalf("Record set length=%d, expected >=0", len(rrset))
} else {
t.Logf("Got %d recordsets: %v", len(rrset), rrset)
}
}
return rrset
}
func getExampleRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet {
rrsets, _ := zone.ResourceRecordSets()
return rrsets.New("www11."+zone.Name(), []string{"10.10.10.10", "169.20.20.20"}, 180, rrstype.A)
}
func getInvalidRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet {
rrsets, _ := zone.ResourceRecordSets()
return rrsets.New("www12."+zone.Name(), []string{"rubbish", "rubbish"}, 180, rrstype.A)
}
func addRrsetOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) {
err := rrsets.StartChangeset().Add(rrset).Apply()
if err != nil {
t.Fatalf("Failed to add recordsets: %v", err)
}
}
/* TestZonesList verifies that listing of zones succeeds */
func TestZonesList(t *testing.T) {
firstZone(t)
}
/* TestZonesID verifies that the id of the zone is returned with the prefix removed */
func TestZonesID(t *testing.T) {
zone := firstZone(t)
// Check /hostedzone/ prefix is removed
zoneID := zone.ID()
if zoneID != zone.Name() {
t.Fatalf("Unexpected zone id: %q", zoneID)
}
}
/* TestZoneAddSuccess verifies that addition of a valid managed DNS zone succeeds */
func TestZoneAddSuccess(t *testing.T) {
testZoneName := "ubernetes.testing"
z := zones(t)
input, err := z.New(testZoneName)
if err != nil {
t.Errorf("Failed to allocate new zone object %s: %v", testZoneName, err)
}
zone, err := z.Add(input)
if err != nil {
t.Errorf("Failed to create new managed DNS zone %s: %v", testZoneName, err)
}
defer func(zone dnsprovider.Zone) {
if zone != nil {
if err := z.Remove(zone); err != nil {
t.Errorf("Failed to delete zone %v: %v", zone, err)
}
}
}(zone)
t.Logf("Successfully added managed DNS zone: %v", zone)
}
/* TestResourceRecordSetsList verifies that listing of RRS's succeeds */
func TestResourceRecordSetsList(t *testing.T) {
listRrsOrFail(t, rrs(t, firstZone(t)))
}
/* TestResourceRecordSetsAddSuccess verifies that addition of a valid RRS succeeds */
func TestResourceRecordSetsAddSuccess(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
set := getExampleRrs(zone)
addRrsetOrFail(t, sets, set)
defer sets.StartChangeset().Remove(set).Apply()
t.Logf("Successfully added resource record set: %v", set)
}
/* TestResourceRecordSetsAdditionVisible verifies that added RRS is visible after addition */
func TestResourceRecordSetsAdditionVisible(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
found := false
for _, record := range listRrsOrFail(t, sets) {
if record.Name() == rrset.Name() {
found = true
break
}
}
if !found {
t.Errorf("Failed to find added resource record set %s", rrset.Name())
}
}
/* TestResourceRecordSetsAddDuplicateFail verifies that addition of a duplicate RRS fails */
func TestResourceRecordSetsAddDuplicateFail(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
// Try to add it again, and verify that the call fails.
err := sets.StartChangeset().Add(rrset).Apply()
if err == nil {
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Should have failed to add duplicate resource record %v, but succeeded instead.", rrset)
} else {
t.Logf("Correctly failed to add duplicate resource record %v: %v", rrset, err)
}
}
/* TestResourceRecordSetsRemove verifies that the removal of an existing RRS succeeds */
func TestResourceRecordSetsRemove(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding", rrset)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
}
/* TestResourceRecordSetsRemoveGone verifies that a removed RRS no longer exists */
func TestResourceRecordSetsRemoveGone(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding", rrset)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
// Check that it's gone
list := listRrsOrFail(t, sets)
found := false
for _, set := range list {
if set.Name() == rrset.Name() {
found = true
break
}
}
if found {
t.Errorf("Deleted resource record set %v is still present", rrset)
}
}
/* TestResourceRecordSetsReplace verifies that replacing an RRS works */
func TestResourceRecordSetsReplace(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplace(t, zone)
}
/* TestResourceRecordSetsReplaceAll verifies that we can remove an RRS and create one with a different name*/
func TestResourceRecordSetsReplaceAll(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplaceAll(t, zone)
}
/* TestResourceRecordSetsHonorsType verifies that we can add records of the same name but different types */
func TestResourceRecordSetsDifferentTypes(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsDifferentTypes(t, zone)
}

View file

@ -0,0 +1,101 @@
/*
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 route53
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordChangeset = &ResourceRecordChangeset{}
type ResourceRecordChangeset struct {
zone *Zone
rrsets *ResourceRecordSets
additions []dnsprovider.ResourceRecordSet
removals []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
}
// buildChange converts a dnsprovider.ResourceRecordSet to a route53.Change request
func buildChange(action string, rrs dnsprovider.ResourceRecordSet) *route53.Change {
change := &route53.Change{
Action: aws.String(action),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: aws.String(rrs.Name()),
Type: aws.String(string(rrs.Type())),
TTL: aws.Int64(rrs.Ttl()),
},
}
for _, rrdata := range rrs.Rrdatas() {
rr := &route53.ResourceRecord{
Value: aws.String(rrdata),
}
change.ResourceRecordSet.ResourceRecords = append(change.ResourceRecordSet.ResourceRecords, rr)
}
return change
}
func (c *ResourceRecordChangeset) Apply() error {
hostedZoneID := c.zone.impl.Id
var changes []*route53.Change
for _, removal := range c.removals {
change := buildChange(route53.ChangeActionDelete, removal)
changes = append(changes, change)
}
for _, addition := range c.additions {
change := buildChange(route53.ChangeActionCreate, addition)
changes = append(changes, change)
}
if len(changes) == 0 {
return nil
}
service := c.zone.zones.interface_.service
request := &route53.ChangeResourceRecordSetsInput{
ChangeBatch: &route53.ChangeBatch{
Changes: changes,
},
HostedZoneId: hostedZoneID,
}
_, err := service.ChangeResourceRecordSets(request)
if err != nil {
// Cast err to awserr.Error to get the Code and
// Message from an error.
return err
}
return nil
}

View file

@ -0,0 +1,53 @@
/*
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 route53
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
"github.com/aws/aws-sdk-go/service/route53"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSet = ResourceRecordSet{}
type ResourceRecordSet struct {
impl *route53.ResourceRecordSet
rrsets *ResourceRecordSets
}
func (rrset ResourceRecordSet) Name() string {
return *rrset.impl.Name
}
func (rrset ResourceRecordSet) Rrdatas() []string {
// Sigh - need to unpack the strings out of the route53 ResourceRecords
result := make([]string, len(rrset.impl.ResourceRecords))
for i, record := range rrset.impl.ResourceRecords {
result[i] = *record.Value
}
return result
}
func (rrset ResourceRecordSet) Ttl() int64 {
return *rrset.impl.TTL
}
func (rrset ResourceRecordSet) Type() rrstype.RrsType {
return rrstype.RrsType(*rrset.impl.Type)
}

View file

@ -0,0 +1,90 @@
/*
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 route53
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSets = ResourceRecordSets{}
type ResourceRecordSets struct {
zone *Zone
}
func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
input := route53.ListResourceRecordSetsInput{
HostedZoneId: rrsets.zone.impl.Id,
}
var list []dnsprovider.ResourceRecordSet
err := rrsets.zone.zones.interface_.service.ListResourceRecordSetsPages(&input, func(page *route53.ListResourceRecordSetsOutput, lastPage bool) bool {
for _, rrset := range page.ResourceRecordSets {
list = append(list, &ResourceRecordSet{rrset, &rrsets})
}
return true
})
if err != nil {
return nil, err
}
return list, nil
}
func (rrsets ResourceRecordSets) Get(name string) (dnsprovider.ResourceRecordSet, error) {
var newRrset dnsprovider.ResourceRecordSet
rrsetList, err := rrsets.List()
if err != nil {
return nil, err
}
for _, rrset := range rrsetList {
if rrset.Name() == name {
newRrset = rrset
break
}
}
return newRrset, nil
}
func (r ResourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset {
return &ResourceRecordChangeset{
zone: r.zone,
rrsets: &r,
}
}
func (r ResourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet {
rrstypeStr := string(rrstype)
rrs := &route53.ResourceRecordSet{
Name: &name,
Type: &rrstypeStr,
TTL: &ttl,
}
for _, rrdata := range rrdatas {
rrs.ResourceRecords = append(rrs.ResourceRecords, &route53.ResourceRecord{
Value: aws.String(rrdata),
})
}
return ResourceRecordSet{
rrs,
&r,
}
}

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["route53api.go"],
tags = ["automanaged"],
deps = [
"//vendor:github.com/aws/aws-sdk-go/aws",
"//vendor:github.com/aws/aws-sdk-go/service/route53",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,133 @@
/*
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.
*/
/* internal implements a stub for the AWS Route53 API, used primarily for unit testing purposes */
package stubs
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
)
// Compile time check for interface conformance
var _ Route53API = &Route53APIStub{}
/* Route53API is the subset of the AWS Route53 API that we actually use. Add methods as required. Signatures must match exactly. */
type Route53API interface {
ListResourceRecordSetsPages(input *route53.ListResourceRecordSetsInput, fn func(p *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool)) error
ChangeResourceRecordSets(*route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error)
ListHostedZonesPages(input *route53.ListHostedZonesInput, fn func(p *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool)) error
CreateHostedZone(*route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error)
DeleteHostedZone(*route53.DeleteHostedZoneInput) (*route53.DeleteHostedZoneOutput, error)
}
// Route53APIStub is a minimal implementation of Route53API, used primarily for unit testing.
// See http://http://docs.aws.amazon.com/sdk-for-go/api/service/route53.html for descriptions
// of all of its methods.
type Route53APIStub struct {
zones map[string]*route53.HostedZone
recordSets map[string]map[string][]*route53.ResourceRecordSet
}
// NewRoute53APIStub returns an initialized Route53APIStub
func NewRoute53APIStub() *Route53APIStub {
return &Route53APIStub{
zones: make(map[string]*route53.HostedZone),
recordSets: make(map[string]map[string][]*route53.ResourceRecordSet),
}
}
func (r *Route53APIStub) ListResourceRecordSetsPages(input *route53.ListResourceRecordSetsInput, fn func(p *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool)) error {
output := route53.ListResourceRecordSetsOutput{} // TODO: Support optional input args.
if len(r.recordSets) <= 0 {
output.ResourceRecordSets = []*route53.ResourceRecordSet{}
} else if _, ok := r.recordSets[*input.HostedZoneId]; !ok {
output.ResourceRecordSets = []*route53.ResourceRecordSet{}
} else {
for _, rrsets := range r.recordSets[*input.HostedZoneId] {
for _, rrset := range rrsets {
output.ResourceRecordSets = append(output.ResourceRecordSets, rrset)
}
}
}
lastPage := true
fn(&output, lastPage)
return nil
}
func (r *Route53APIStub) ChangeResourceRecordSets(input *route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) {
output := &route53.ChangeResourceRecordSetsOutput{}
recordSets, ok := r.recordSets[*input.HostedZoneId]
if !ok {
recordSets = make(map[string][]*route53.ResourceRecordSet)
}
for _, change := range input.ChangeBatch.Changes {
key := *change.ResourceRecordSet.Name + "::" + *change.ResourceRecordSet.Type
switch *change.Action {
case route53.ChangeActionCreate:
if _, found := recordSets[key]; found {
return nil, fmt.Errorf("Attempt to create duplicate rrset %s", key) // TODO: Return AWS errors with codes etc
}
recordSets[key] = append(recordSets[key], change.ResourceRecordSet)
case route53.ChangeActionDelete:
if _, found := recordSets[key]; !found {
return nil, fmt.Errorf("Attempt to delete non-existent rrset %s", key) // TODO: Check other fields too
}
delete(recordSets, key)
case route53.ChangeActionUpsert:
// TODO - not used yet
}
}
r.recordSets[*input.HostedZoneId] = recordSets
return output, nil // TODO: We should ideally return status etc, but we don't' use that yet.
}
func (r *Route53APIStub) ListHostedZonesPages(input *route53.ListHostedZonesInput, fn func(p *route53.ListHostedZonesOutput, lastPage bool) (shouldContinue bool)) error {
output := &route53.ListHostedZonesOutput{}
for _, zone := range r.zones {
output.HostedZones = append(output.HostedZones, zone)
}
lastPage := true
fn(output, lastPage)
return nil
}
func (r *Route53APIStub) CreateHostedZone(input *route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) {
name := aws.StringValue(input.Name)
id := "/hostedzone/" + name
if _, ok := r.zones[id]; ok {
return nil, fmt.Errorf("Error creating hosted DNS zone: %s already exists", id)
}
r.zones[id] = &route53.HostedZone{
Id: aws.String(id),
Name: aws.String(name),
}
return &route53.CreateHostedZoneOutput{HostedZone: r.zones[id]}, nil
}
func (r *Route53APIStub) DeleteHostedZone(input *route53.DeleteHostedZoneInput) (*route53.DeleteHostedZoneOutput, error) {
if _, ok := r.zones[*input.Id]; !ok {
return nil, fmt.Errorf("Error deleting hosted DNS zone: %s does not exist", *input.Id)
}
if len(r.recordSets[*input.Id]) > 0 {
return nil, fmt.Errorf("Error deleting hosted DNS zone: %s has resource records", *input.Id)
}
delete(r.zones, *input.Id)
return &route53.DeleteHostedZoneOutput{}, nil
}

View file

@ -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 route53
import (
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/route53"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
)
// Compile time check for interface adherence
var _ dnsprovider.Zone = &Zone{}
type Zone struct {
impl *route53.HostedZone
zones *Zones
}
func (zone *Zone) Name() string {
return aws.StringValue(zone.impl.Name)
}
func (zone *Zone) ID() string {
id := aws.StringValue(zone.impl.Id)
id = strings.TrimPrefix(id, "/hostedzone/")
return id
}
func (zone *Zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) {
return &ResourceRecordSets{zone}, true
}

View file

@ -0,0 +1,73 @@
/*
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 route53
import (
"github.com/aws/aws-sdk-go/service/route53"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/pkg/util/uuid"
)
// Compile time check for interface adherence
var _ dnsprovider.Zones = Zones{}
type Zones struct {
interface_ *Interface
}
func (zones Zones) List() ([]dnsprovider.Zone, error) {
var zoneList []dnsprovider.Zone
input := route53.ListHostedZonesInput{}
err := zones.interface_.service.ListHostedZonesPages(&input, func(page *route53.ListHostedZonesOutput, lastPage bool) bool {
for _, zone := range page.HostedZones {
zoneList = append(zoneList, &Zone{zone, &zones})
}
return true
})
if err != nil {
return []dnsprovider.Zone{}, err
}
return zoneList, nil
}
func (zones Zones) Add(zone dnsprovider.Zone) (dnsprovider.Zone, error) {
dnsName := zone.Name()
callerReference := string(uuid.NewUUID())
input := route53.CreateHostedZoneInput{Name: &dnsName, CallerReference: &callerReference}
output, err := zones.interface_.service.CreateHostedZone(&input)
if err != nil {
return nil, err
}
return &Zone{output.HostedZone, &zones}, nil
}
func (zones Zones) Remove(zone dnsprovider.Zone) error {
zoneId := zone.(*Zone).impl.Id
input := route53.DeleteHostedZoneInput{Id: zoneId}
_, err := zones.interface_.service.DeleteHostedZone(&input)
if err != nil {
return err
}
return nil
}
func (zones Zones) New(name string) (dnsprovider.Zone, error) {
id := string(uuid.NewUUID())
managedZone := route53.HostedZone{Id: &id, Name: &name}
return &Zone{&managedZone, &zones}, nil
}

View file

@ -0,0 +1,62 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"coredns.go",
"interface.go",
"rrchangeset.go",
"rrset.go",
"rrsets.go",
"zone.go",
"zones.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/providers/coredns/stubs:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//vendor:github.com/coreos/etcd/client",
"//vendor:github.com/golang/glog",
"//vendor:github.com/miekg/coredns/middleware/etcd/msg",
"//vendor:golang.org/x/net/context",
"//vendor:gopkg.in/gcfg.v1",
],
)
go_test(
name = "go_default_test",
srcs = ["coredns_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/providers/coredns/stubs:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//federation/pkg/dnsprovider/tests:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//federation/pkg/dnsprovider/providers/coredns/stubs:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,94 @@
/*
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 coredns is the implementation of pkg/dnsprovider interface for CoreDNS
package coredns
import (
"fmt"
etcdc "github.com/coreos/etcd/client"
"github.com/golang/glog"
"gopkg.in/gcfg.v1"
"io"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"strconv"
"strings"
)
// "coredns" should be used to use this DNS provider
const (
ProviderName = "coredns"
)
// Config to override defaults
type Config struct {
Global struct {
EtcdEndpoints string `gcfg:"etcd-endpoints"`
DNSZones string `gcfg:"zones"`
}
}
func init() {
dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) {
return newCoreDNSProviderInterface(config)
})
}
// newCoreDnsProviderInterface creates a new instance of an CoreDNS DNS Interface.
func newCoreDNSProviderInterface(config io.Reader) (*Interface, error) {
etcdEndpoints := "http://federation-dns-server-etcd:2379"
etcdPathPrefix := "skydns"
dnsZones := ""
// Possibly override defaults with config below
if config != nil {
var cfg Config
if err := gcfg.ReadInto(&cfg, config); err != nil {
glog.Errorf("Couldn't read config: %v", err)
return nil, err
}
etcdEndpoints = cfg.Global.EtcdEndpoints
dnsZones = cfg.Global.DNSZones
}
glog.Infof("Using CoreDNS DNS provider")
if dnsZones == "" {
return nil, fmt.Errorf("Need to provide at least one DNS Zone")
}
etcdCfg := etcdc.Config{
Endpoints: strings.Split(etcdEndpoints, ","),
Transport: etcdc.DefaultTransport,
}
c, err := etcdc.New(etcdCfg)
if err != nil {
return nil, fmt.Errorf("Create etcd client from the config failed")
}
etcdKeysAPI := etcdc.NewKeysAPI(c)
intf := newInterfaceWithStub(etcdKeysAPI)
intf.etcdPathPrefix = etcdPathPrefix
zoneList := strings.Split(dnsZones, ",")
intf.zones = Zones{intf: intf}
for index, zoneName := range zoneList {
zone := Zone{domain: zoneName, id: strconv.Itoa(index), zones: &intf.zones}
intf.zones.zoneList = append(intf.zones.zoneList, zone)
}
return intf, nil
}

View file

@ -0,0 +1,261 @@
/*
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 coredns
import (
"flag"
"fmt"
"os"
"strconv"
"testing"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
corednstesting "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/coredns/stubs"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
"k8s.io/kubernetes/federation/pkg/dnsprovider/tests"
"strings"
)
func newTestInterface() (dnsprovider.Interface, error) {
// Use this to test the real cloud service.
// return dnsprovider.GetDnsProvider(ProviderName, strings.NewReader("\n[global]\nproject-id = federation0-cluster00"))
return newFakeInterface() // Use this to stub out the entire cloud service
}
func newFakeInterface() (dnsprovider.Interface, error) {
var service corednstesting.EtcdKeysAPI
service = corednstesting.NewEtcdKeysAPIStub()
intf := newInterfaceWithStub(service)
intf.etcdPathPrefix = "skydns"
zoneList := strings.Split("example.com,federation.io", ",")
intf.zones = Zones{intf: intf}
for index, zoneName := range zoneList {
zone := Zone{domain: zoneName, id: strconv.Itoa(index), zones: &intf.zones}
intf.zones.zoneList = append(intf.zones.zoneList, zone)
}
return intf, nil
}
var intf dnsprovider.Interface
func TestMain(m *testing.M) {
fmt.Printf("Parsing flags.\n")
flag.Parse()
var err error
fmt.Printf("Getting new test interface.\n")
intf, err = newTestInterface()
if err != nil {
fmt.Printf("Error creating interface: %v", err)
os.Exit(1)
}
fmt.Printf("Running tests...\n")
os.Exit(m.Run())
}
// zones returns the zones interface for the configured dns provider account/project,
// or fails if it can't be found
func zones(t *testing.T) dnsprovider.Zones {
zonesInterface, supported := intf.Zones()
if !supported {
t.Fatalf("Zones interface not supported by interface %v", intf)
} else {
t.Logf("Got zones %v\n", zonesInterface)
}
return zonesInterface
}
// firstZone returns the first zone for the configured dns provider account/project,
// or fails if it can't be found
func firstZone(t *testing.T) dnsprovider.Zone {
t.Logf("Getting zones")
z := zones(t)
zones, err := z.List()
if err != nil {
t.Fatalf("Failed to list zones: %v", err)
} else {
t.Logf("Got zone list: %v\n", zones)
}
if len(zones) < 1 {
t.Fatalf("Zone listing returned %d, expected >= %d", len(zones), 1)
} else {
t.Logf("Got at least 1 zone in list:%v\n", zones[0])
}
return zones[0]
}
/* rrs returns the ResourceRecordSets interface for a given zone */
func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) {
rrsets, supported := zone.ResourceRecordSets()
if !supported {
t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone)
return r
}
return rrsets
}
func listRrsOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets) []dnsprovider.ResourceRecordSet {
rrset, err := rrsets.List()
if err != nil {
t.Fatalf("Failed to list recordsets: %v", err)
} else {
if len(rrset) < 0 {
t.Fatalf("Record set length=%d, expected >=0", len(rrset))
} else {
t.Logf("Got %d recordsets: %v", len(rrset), rrset)
}
}
return rrset
}
func getRrOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, name string) dnsprovider.ResourceRecordSet {
rrset, err := rrsets.Get(name)
if err != nil {
t.Fatalf("Failed to get recordset: %v", err)
} else if rrset == nil {
t.Logf("Did not Get recordset: %v", name)
} else {
t.Logf("Got recordset: %v", rrset.Name())
}
return rrset
}
func getExampleRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet {
rrsets, _ := zone.ResourceRecordSets()
return rrsets.New("www11."+zone.Name(), []string{"10.10.10.10", "169.20.20.20"}, 180, rrstype.A)
}
func addRrsetOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) {
err := rrsets.StartChangeset().Add(rrset).Apply()
if err != nil {
t.Fatalf("Failed to add recordsets: %v", err)
}
}
/* TestZonesList verifies that listing of zones succeeds */
func TestZonesList(t *testing.T) {
firstZone(t)
}
/* TestZonesID verifies that the id of the zone is unique */
func TestZonesID(t *testing.T) {
zone := firstZone(t)
zoneID := zone.ID()
if zoneID != "0" {
t.Fatalf("Unexpected zone id: %q", zoneID)
}
}
/* TestResourceRecordSetsGet verifies that getting of RRS succeeds */
func TestResourceRecordSetsGet(t *testing.T) {
getRrOrFail(t, rrs(t, firstZone(t)), "example.com")
}
/* TestResourceRecordSetsAddSuccess verifies that addition of a valid RRS succeeds */
func TestResourceRecordSetsAddSuccess(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
set := getExampleRrs(zone)
addRrsetOrFail(t, sets, set)
defer sets.StartChangeset().Remove(set).Apply()
t.Logf("Successfully added resource record set: %v", set)
}
/* TestResourceRecordSetsAdditionVisible verifies that added RRS is visible after addition */
func TestResourceRecordSetsAdditionVisible(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
record := getRrOrFail(t, sets, rrset.Name())
if record == nil {
t.Errorf("Failed to find added resource record set %s", rrset.Name())
}
}
/* TestResourceRecordSetsAddDuplicateFail verifies that addition of a duplicate RRS fails */
func TestResourceRecordSetsAddDuplicateFail(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
// Try to add it again, and verify that the call fails.
err := sets.StartChangeset().Add(rrset).Apply()
if err == nil {
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Should have failed to add duplicate resource record %v, but succeeded instead.", rrset)
} else {
t.Logf("Correctly failed to add duplicate resource record %v: %v", rrset, err)
}
}
/* TestResourceRecordSetsRemove verifies that the removal of an existing RRS succeeds */
func TestResourceRecordSetsRemove(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding", rrset)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
}
/* TestResourceRecordSetsRemoveGone verifies that a removed RRS no longer exists */
func TestResourceRecordSetsRemoveGone(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding", rrset)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
record := getRrOrFail(t, sets, rrset.Name())
if record != nil {
t.Errorf("Deleted resource record set %v is still present", rrset)
}
}
/* TestResourceRecordSetsReplace verifies that replacing an RRS works */
func TestResourceRecordSetsReplace(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplace(t, zone)
}
/* TestResourceRecordSetsReplaceAll verifies that we can remove an RRS and create one with a different name*/
func TestResourceRecordSetsReplaceAll(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplaceAll(t, zone)
}

View file

@ -0,0 +1,41 @@
/*
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 coredns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/coredns/stubs"
)
// Compile time check for interface adherence
var _ dnsprovider.Interface = Interface{}
type Interface struct {
etcdKeysAPI stubs.EtcdKeysAPI
etcdPathPrefix string
zones Zones
}
// newInterfaceWithStub facilitates stubbing out the underlying etcd
// library for testing purposes. It returns an provider-independent interface.
func newInterfaceWithStub(etcdKeysAPI stubs.EtcdKeysAPI) *Interface {
return &Interface{etcdKeysAPI: etcdKeysAPI}
}
func (i Interface) Zones() (dnsprovider.Zones, bool) {
return i.zones, true
}

View file

@ -0,0 +1,123 @@
/*
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 coredns
import (
"encoding/json"
"fmt"
etcdc "github.com/coreos/etcd/client"
dnsmsg "github.com/miekg/coredns/middleware/etcd/msg"
"golang.org/x/net/context"
"hash/fnv"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordChangeset = &ResourceRecordChangeset{}
type ChangeSetType string
const (
ADDITION = ChangeSetType("ADDITION")
DELETION = ChangeSetType("DELETION")
)
type ChangeSet struct {
cstype ChangeSetType
rrset dnsprovider.ResourceRecordSet
}
type ResourceRecordChangeset struct {
zone *Zone
rrsets *ResourceRecordSets
changeset []ChangeSet
}
func (c *ResourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
c.changeset = append(c.changeset, ChangeSet{cstype: ADDITION, rrset: rrset})
return c
}
func (c *ResourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
c.changeset = append(c.changeset, ChangeSet{cstype: DELETION, rrset: rrset})
return c
}
func (c *ResourceRecordChangeset) Apply() error {
ctx := context.Background()
etcdPathPrefix := c.zone.zones.intf.etcdPathPrefix
getOpts := &etcdc.GetOptions{}
setOpts := &etcdc.SetOptions{}
deleteOpts := &etcdc.DeleteOptions{
Recursive: true,
}
for _, changeset := range c.changeset {
switch changeset.cstype {
case ADDITION:
for _, rrdata := range changeset.rrset.Rrdatas() {
b, err := json.Marshal(&dnsmsg.Service{Host: rrdata, TTL: uint32(changeset.rrset.Ttl()), Group: changeset.rrset.Name()})
if err != nil {
return err
}
recordValue := string(b)
recordLabel := getHash(rrdata)
recordKey := buildDNSNameString(changeset.rrset.Name(), recordLabel)
response, err := c.zone.zones.intf.etcdKeysAPI.Get(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), getOpts)
if err == nil && response != nil {
return fmt.Errorf("Key already exist, key: %v", recordKey)
}
_, err = c.zone.zones.intf.etcdKeysAPI.Set(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), recordValue, setOpts)
if err != nil {
return err
}
}
case DELETION:
for _, rrdata := range changeset.rrset.Rrdatas() {
recordLabel := getHash(rrdata)
recordKey := buildDNSNameString(changeset.rrset.Name(), recordLabel)
_, err := c.zone.zones.intf.etcdKeysAPI.Delete(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), deleteOpts)
if err != nil {
return err
}
}
// TODO: We need to cleanup empty dirs in etcd
}
}
return nil
}
func getHash(text string) string {
h := fnv.New32a()
h.Write([]byte(text))
return fmt.Sprintf("%x", h.Sum32())
}
func buildDNSNameString(labels ...string) string {
var res string
for _, label := range labels {
if res == "" {
res = label
} else {
res = fmt.Sprintf("%s.%s", label, res)
}
}
return res
}

View file

@ -0,0 +1,49 @@
/*
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 coredns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSet = ResourceRecordSet{}
type ResourceRecordSet struct {
name string
rrdatas []string
ttl int64
rrsType rrstype.RrsType
rrsets *ResourceRecordSets
}
func (rrset ResourceRecordSet) Name() string {
return rrset.name
}
func (rrset ResourceRecordSet) Rrdatas() []string {
return rrset.rrdatas
}
func (rrset ResourceRecordSet) Ttl() int64 {
return rrset.ttl
}
func (rrset ResourceRecordSet) Type() rrstype.RrsType {
return rrset.rrsType
}

View file

@ -0,0 +1,109 @@
/*
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 coredns
import (
"encoding/json"
"fmt"
etcdc "github.com/coreos/etcd/client"
"github.com/golang/glog"
dnsmsg "github.com/miekg/coredns/middleware/etcd/msg"
"golang.org/x/net/context"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
"net"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSets = ResourceRecordSets{}
type ResourceRecordSets struct {
zone *Zone
}
func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
var list []dnsprovider.ResourceRecordSet
return list, fmt.Errorf("OperationNotSupported")
}
func (rrsets ResourceRecordSets) Get(name string) (dnsprovider.ResourceRecordSet, error) {
getOpts := &etcdc.GetOptions{
Recursive: true,
}
etcdPathPrefix := rrsets.zone.zones.intf.etcdPathPrefix
response, err := rrsets.zone.zones.intf.etcdKeysAPI.Get(context.Background(), dnsmsg.Path(name, etcdPathPrefix), getOpts)
if err != nil {
if etcdc.IsKeyNotFound(err) {
glog.V(2).Infof("Subdomain %q does not exist", name)
return nil, nil
}
return nil, fmt.Errorf("Failed to get service from etcd, err: %v", err)
}
if emptyResponse(response) {
glog.V(2).Infof("Subdomain %q does not exist in etcd", name)
return nil, nil
}
rrset := ResourceRecordSet{name: name, rrdatas: []string{}, rrsets: &rrsets}
found := false
for _, node := range response.Node.Nodes {
found = true
service := dnsmsg.Service{}
err = json.Unmarshal([]byte(node.Value), &service)
if err != nil {
return nil, fmt.Errorf("Failed to unmarshall json data, err: %v", err)
}
// assuming all rrdatas in a rrset will have same type
ip := net.ParseIP(service.Host)
switch {
case ip == nil:
rrset.rrsType = rrstype.CNAME
case ip.To4() != nil:
rrset.rrsType = rrstype.A
}
rrset.rrdatas = append(rrset.rrdatas, service.Host)
rrset.ttl = int64(service.TTL)
}
if !found {
return nil, nil
}
return rrset, 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 {
return ResourceRecordSet{
name: name,
rrdatas: rrdatas,
ttl: ttl,
rrsType: rrsType,
rrsets: &rrsets,
}
}
func emptyResponse(resp *etcdc.Response) bool {
return resp == nil || resp.Node == nil || (len(resp.Node.Value) == 0 && len(resp.Node.Nodes) == 0)
}

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["corednsapi.go"],
tags = ["automanaged"],
deps = [
"//vendor:github.com/coreos/etcd/client",
"//vendor:golang.org/x/net/context",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,84 @@
/*
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 stubs implements a stub for the EtcdKeysAPI, used primarily for unit testing purposes
package stubs
import (
etcd "github.com/coreos/etcd/client"
"golang.org/x/net/context"
"strings"
)
// Compile time check for interface conformance
var _ EtcdKeysAPI = &EtcdKeysAPIStub{}
type EtcdKeysAPI interface {
Set(context context.Context, key, value string, options *etcd.SetOptions) (*etcd.Response, error)
Get(context context.Context, key string, options *etcd.GetOptions) (*etcd.Response, error)
Delete(context context.Context, key string, options *etcd.DeleteOptions) (*etcd.Response, error)
}
type EtcdKeysAPIStub struct {
writes map[string]string
}
// NewEtcdKeysAPIStub returns an initialized EtcdKeysAPIStub
func NewEtcdKeysAPIStub() *EtcdKeysAPIStub {
return &EtcdKeysAPIStub{make(map[string]string)}
}
func (ec *EtcdKeysAPIStub) Set(context context.Context, key, value string, options *etcd.SetOptions) (*etcd.Response, error) {
ec.writes[key] = value
return nil, nil
}
func (ec *EtcdKeysAPIStub) Delete(context context.Context, key string, options *etcd.DeleteOptions) (*etcd.Response, error) {
for p := range ec.writes {
if (options.Recursive && strings.HasPrefix(p, key)) || (!options.Recursive && p == key) {
delete(ec.writes, p)
}
}
return nil, nil
}
func (ec *EtcdKeysAPIStub) Get(context context.Context, key string, options *etcd.GetOptions) (*etcd.Response, error) {
nodes := ec.GetAll(key)
if len(nodes) == 0 {
return nil, nil
}
if len(nodes) == 1 && nodes[key] != "" {
return &etcd.Response{Node: &etcd.Node{Key: key, Value: nodes[key], Dir: false}}, nil
}
node := &etcd.Node{Key: key, Dir: true, Nodes: etcd.Nodes{}}
for k, v := range nodes {
n := &etcd.Node{Key: k, Value: v}
node.Nodes = append(node.Nodes, n)
}
return &etcd.Response{Node: node}, nil
}
func (ec *EtcdKeysAPIStub) GetAll(key string) map[string]string {
nodes := make(map[string]string)
key = strings.ToLower(key)
for path := range ec.writes {
if strings.HasPrefix(path, key) {
nodes[path] = ec.writes[path]
}
}
return nodes
}

View file

@ -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 coredns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
)
// Compile time check for interface adherence
var _ dnsprovider.Zone = Zone{}
type Zone struct {
domain string
id string
zones *Zones
}
func (zone Zone) Name() string {
return zone.domain
}
func (zone Zone) ID() string {
return zone.id
}
func (zone Zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) {
return &ResourceRecordSets{zone: &zone}, true
}

View file

@ -0,0 +1,49 @@
/*
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 coredns
import (
"fmt"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
)
// Compile time check for interface adherence
var _ dnsprovider.Zones = Zones{}
type Zones struct {
intf *Interface
zoneList []Zone
}
func (zones Zones) List() ([]dnsprovider.Zone, error) {
var zoneList []dnsprovider.Zone
for _, zone := range zones.zoneList {
zoneList = append(zoneList, zone)
}
return zoneList, nil
}
func (zones Zones) Add(zone dnsprovider.Zone) (dnsprovider.Zone, error) {
return &Zone{}, fmt.Errorf("OperationNotSupported")
}
func (zones Zones) Remove(zone dnsprovider.Zone) error {
return fmt.Errorf("OperationNotSupported")
}
func (zones Zones) New(name string) (dnsprovider.Zone, error) {
return &Zone{}, fmt.Errorf("OperationNotSupported")
}

View file

@ -0,0 +1,66 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"clouddns.go",
"interface.go",
"rrchangeset.go",
"rrset.go",
"rrsets.go",
"zone.go",
"zones.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal:go_default_library",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces:go_default_library",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//pkg/cloudprovider/providers/gce:go_default_library",
"//vendor:cloud.google.com/go/compute/metadata",
"//vendor:github.com/golang/glog",
"//vendor:golang.org/x/oauth2",
"//vendor:golang.org/x/oauth2/google",
"//vendor:google.golang.org/api/compute/v1",
"//vendor:google.golang.org/api/dns/v1",
"//vendor:gopkg.in/gcfg.v1",
],
)
go_test(
name = "go_default_test",
srcs = ["clouddns_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//federation/pkg/dnsprovider/tests:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,116 @@
/*
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.
*/
// clouddns is the implementation of pkg/dnsprovider interface for Google Cloud DNS
package clouddns
import (
"io"
"cloud.google.com/go/compute/metadata"
"github.com/golang/glog"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
dns "google.golang.org/api/dns/v1"
gcfg "gopkg.in/gcfg.v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
)
const (
ProviderName = "google-clouddns"
)
func init() {
dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) {
return newCloudDns(config)
})
}
type Config struct {
Global struct {
TokenURL string `gcfg:"token-url"`
TokenBody string `gcfg:"token-body"`
ProjectID string `gcfg:"project-id"`
}
}
// newCloudDns creates a new instance of a Google Cloud DNS Interface.
func newCloudDns(config io.Reader) (*Interface, error) {
projectID, _ := metadata.ProjectID() // On error we get an empty string, which is fine for now.
var tokenSource oauth2.TokenSource
// Possibly override defaults with config below
if config != nil {
var cfg Config
if err := gcfg.ReadInto(&cfg, config); err != nil {
glog.Errorf("Couldn't read config: %v", err)
return nil, err
}
glog.Infof("Using Google Cloud DNS provider config %+v", cfg)
if cfg.Global.ProjectID != "" {
projectID = cfg.Global.ProjectID
}
if cfg.Global.TokenURL != "" {
tokenSource = gce.NewAltTokenSource(cfg.Global.TokenURL, cfg.Global.TokenBody)
}
}
return CreateInterface(projectID, tokenSource)
}
// CreateInterface creates a clouddns.Interface object using the specified parameters.
// If no tokenSource is specified, uses oauth2.DefaultTokenSource.
func CreateInterface(projectID string, tokenSource oauth2.TokenSource) (*Interface, error) {
if tokenSource == nil {
var err error
tokenSource, err = google.DefaultTokenSource(
oauth2.NoContext,
compute.CloudPlatformScope,
compute.ComputeScope)
glog.Infof("Using DefaultTokenSource %#v", tokenSource)
if err != nil {
return nil, err
}
} else {
glog.Infof("Using existing Token Source %#v", tokenSource)
}
oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
service, err := dns.New(oauthClient)
if err != nil {
glog.Errorf("Failed to get Cloud DNS client: %v", err)
}
glog.Infof("Successfully got DNS service: %v\n", service)
return newInterfaceWithStub(projectID, internal.NewService(service)), nil
}
// NewFakeInterface returns a fake clouddns interface, useful for unit testing purposes.
func NewFakeInterface() (dnsprovider.Interface, error) {
service := stubs.NewService()
interface_ := newInterfaceWithStub("", service)
zones := service.ManagedZones_
// Add a fake zone to test against.
zone := &stubs.ManagedZone{Service: zones, Name_: "example.com", Rrsets: []stubs.ResourceRecordSet{}, Id_: 1}
call := zones.Create(interface_.project(), zone)
if _, err := call.Do(); err != nil {
return nil, err
}
return interface_, nil
}

View file

@ -0,0 +1,273 @@
/*
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 clouddns
import (
"flag"
"fmt"
"os"
"testing"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
"k8s.io/kubernetes/federation/pkg/dnsprovider/tests"
)
func newTestInterface() (dnsprovider.Interface, error) {
// Use this to test the real cloud service - insert appropriate project-id. Default token source will be used. See
// https://github.com/golang/oauth2/blob/master/google/default.go for details.
// return dnsprovider.GetDnsProvider(ProviderName, strings.NewReader("\n[global]\nproject-id = federation0-cluster00"))
return NewFakeInterface() // Use this to stub out the entire cloud service
}
var interface_ dnsprovider.Interface
func TestMain(m *testing.M) {
flag.Parse()
var err error
interface_, err = newTestInterface()
if err != nil {
fmt.Printf("Error creating interface: %v", err)
os.Exit(1)
}
os.Exit(m.Run())
}
// zones returns the zones interface for the configured dns provider account/project,
// or fails if it can't be found
func zones(t *testing.T) dnsprovider.Zones {
zonesInterface, supported := interface_.Zones()
if !supported {
t.Fatalf("Zones interface not supported by interface %v", interface_)
} else {
t.Logf("Got zones %v\n", zonesInterface)
}
return zonesInterface
}
// firstZone returns the first zone for the configured dns provider account/project,
// or fails if it can't be found
func firstZone(t *testing.T) dnsprovider.Zone {
t.Logf("Getting zones")
zones, err := zones(t).List()
if err != nil {
t.Fatalf("Failed to list zones: %v", err)
} else {
t.Logf("Got zone list: %v\n", zones)
}
if len(zones) < 1 {
t.Fatalf("Zone listing returned %d, expected >= %d", len(zones), 1)
} else {
t.Logf("Got at least 1 zone in list:%v\n", zones[0])
}
return zones[0]
}
/* rrs returns the ResourceRecordSets interface for a given zone */
func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) {
rrsets, supported := zone.ResourceRecordSets()
if !supported {
t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone)
return r
}
return rrsets
}
func listRrsOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets) []dnsprovider.ResourceRecordSet {
rrset, err := rrsets.List()
if err != nil {
t.Fatalf("Failed to list recordsets: %v", err)
} else {
if len(rrset) < 0 {
t.Fatalf("Record set length=%d, expected >=0", len(rrset))
} else {
t.Logf("Got %d recordsets: %v", len(rrset), rrset)
}
}
return rrset
}
func getExampleRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet {
rrsets, _ := zone.ResourceRecordSets()
return rrsets.New("www11."+zone.Name(), []string{"10.10.10.10", "169.20.20.20"}, 180, rrstype.A)
}
func getInvalidRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet {
rrsets, _ := zone.ResourceRecordSets()
return rrsets.New("www12."+zone.Name(), []string{"rubbish", "rubbish"}, 180, rrstype.A)
}
func addRrsetOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) {
err := rrsets.StartChangeset().Add(rrset).Apply()
if err != nil {
t.Fatalf("Failed to add recordsets: %v", err)
}
}
/* TestZonesList verifies that listing of zones succeeds */
func TestZonesList(t *testing.T) {
firstZone(t)
}
/* TestZonesID verifies that the id of the zone is returned with the prefix removed */
func TestZonesID(t *testing.T) {
zone := firstZone(t)
zoneID := zone.ID()
if zoneID != "1" {
t.Fatalf("Unexpected zone id: %q", zoneID)
}
}
/* TestZoneAddSuccess verifies that addition of a valid managed DNS zone succeeds */
func TestZoneAddSuccess(t *testing.T) {
testZoneName := "ubernetesv2.test."
t.Logf("Getting zones")
z := zones(t)
t.Logf("Got zones, making new Zone")
input, err := z.New(testZoneName)
if err != nil {
t.Errorf("Failed to allocate new zone object %s: %v", testZoneName, err)
}
zone, err := z.Add(input)
if err != nil {
t.Errorf("Failed to create new managed DNS zone %s: %v", testZoneName, err)
}
defer func(zone dnsprovider.Zone) {
if zone != nil {
if err := z.Remove(zone); err != nil {
t.Errorf("Failed to delete zone %v: %v", zone, err)
}
}
}(zone)
t.Logf("Successfully added managed DNS zone: %v", zone)
}
/* TestResourceRecordSetsList verifies that listing of RRS's succeeds */
func TestResourceRecordSetsList(t *testing.T) {
listRrsOrFail(t, rrs(t, firstZone(t)))
}
/* TestResourceRecordSetsAddSuccess verifies that addition of a valid RRS succeeds */
func TestResourceRecordSetsAddSuccess(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
set := getExampleRrs(zone)
addRrsetOrFail(t, sets, set)
defer sets.StartChangeset().Remove(set).Apply()
t.Logf("Successfully added resource record set: %v", set)
}
/* TestResourceRecordSetsAdditionVisible verifies that added RRS is visible after addition */
func TestResourceRecordSetsAdditionVisible(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
found := false
for _, record := range listRrsOrFail(t, sets) {
if record.Name() == rrset.Name() {
found = true
break
}
}
if !found {
t.Errorf("Failed to find added resource record set %s", rrset.Name())
}
}
/* TestResourceRecordSetsAddDuplicateFail verifies that addition of a duplicate RRS fails */
func TestResourceRecordSetsAddDuplicateFail(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
// Try to add it again, and verify that the call fails.
err := sets.StartChangeset().Add(rrset).Apply()
if err == nil {
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Should have failed to add duplicate resource record %v, but succeeded instead.", rrset)
} else {
t.Logf("Correctly failed to add duplicate resource record %v: %v", rrset, err)
}
}
/* TestResourceRecordSetsRemove verifies that the removal of an existing RRS succeeds */
func TestResourceRecordSetsRemove(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding: %v", rrset, err)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
}
/* TestResourceRecordSetsRemoveGone verifies that a removed RRS no longer exists */
func TestResourceRecordSetsRemoveGone(t *testing.T) {
zone := firstZone(t)
sets := rrs(t, zone)
rrset := getExampleRrs(zone)
addRrsetOrFail(t, sets, rrset)
err := sets.StartChangeset().Remove(rrset).Apply()
if err != nil {
// Try again to clean up.
defer sets.StartChangeset().Remove(rrset).Apply()
t.Errorf("Failed to remove resource record set %v after adding: %v", rrset, err)
} else {
t.Logf("Successfully removed resource set %v after adding", rrset)
}
// Check that it's gone
list := listRrsOrFail(t, sets)
found := false
for _, set := range list {
if set.Name() == rrset.Name() {
found = true
break
}
}
if found {
t.Errorf("Deleted resource record set %v is still present", rrset)
}
}
/* TestResourceRecordSetsReplace verifies that replacing an RRS works */
func TestResourceRecordSetsReplace(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplace(t, zone)
}
/* TestResourceRecordSetsReplaceAll verifies that we can remove an RRS and create one with a different name*/
func TestResourceRecordSetsReplaceAll(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsReplaceAll(t, zone)
}
/* TestResourceRecordSetsHonorsType verifies that we can add records of the same name but different types */
func TestResourceRecordSetsDifferentTypes(t *testing.T) {
zone := firstZone(t)
tests.CommonTestResourceRecordSetsDifferentTypes(t, zone)
}

View file

@ -0,0 +1,43 @@
/*
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 clouddns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
var _ dnsprovider.Interface = Interface{}
type Interface struct {
project_ string
service interfaces.Service
}
// newInterfaceWithStub facilitates stubbing out the underlying Google Cloud DNS
// library for testing purposes. It returns an provider-independent interface.
func newInterfaceWithStub(project string, service interfaces.Service) *Interface {
return &Interface{project, service}
}
func (i Interface) Zones() (zones dnsprovider.Zones, supported bool) {
return Zones{i.service.ManagedZones(), &i}, true
}
func (i Interface) project() string {
return i.project_
}

View file

@ -0,0 +1,55 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"change.go",
"changes_create_call.go",
"changes_service.go",
"clouddns.go",
"managed_zone.go",
"managed_zone_create_call.go",
"managed_zones_delete_call.go",
"managed_zones_get_call.go",
"managed_zones_list_call.go",
"managed_zones_list_response.go",
"managed_zones_service.go",
"rrset.go",
"rrsets_list_call.go",
"rrsets_list_response.go",
"rrsets_service.go",
"service.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//pkg/util/uuid:go_default_library",
"//vendor:google.golang.org/api/dns/v1",
"//vendor:google.golang.org/api/googleapi",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces:all-srcs",
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs:all-srcs",
],
tags = ["automanaged"],
)

View file

@ -0,0 +1,43 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adeherence
var _ interfaces.Change = Change{}
type Change struct{ impl *dns.Change }
func (c Change) Additions() (rrsets []interfaces.ResourceRecordSet) {
rrsets = make([]interfaces.ResourceRecordSet, len(c.impl.Additions))
for index, addition := range c.impl.Additions {
rrsets[index] = interfaces.ResourceRecordSet(&ResourceRecordSet{addition})
}
return rrsets
}
func (c Change) Deletions() (rrsets []interfaces.ResourceRecordSet) {
rrsets = make([]interfaces.ResourceRecordSet, len(c.impl.Deletions))
for index, deletion := range c.impl.Deletions {
rrsets[index] = interfaces.ResourceRecordSet(&ResourceRecordSet{deletion})
}
return rrsets
}

View file

@ -0,0 +1,34 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ChangesCreateCall = ChangesCreateCall{}
type ChangesCreateCall struct{ impl *dns.ChangesCreateCall }
func (c ChangesCreateCall) Do(opts ...googleapi.CallOption) (interfaces.Change, error) {
ch, err := c.impl.Do(opts...)
return &Change{ch}, err
}

View file

@ -0,0 +1,43 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ChangesService = ChangesService{}
type ChangesService struct{ impl *dns.ChangesService }
func (c ChangesService) Create(project string, managedZone string, change interfaces.Change) interfaces.ChangesCreateCall {
return &ChangesCreateCall{c.impl.Create(project, managedZone, change.(*Change).impl)}
}
func (c ChangesService) NewChange(additions, deletions []interfaces.ResourceRecordSet) interfaces.Change {
adds := make([]*dns.ResourceRecordSet, len(additions))
deletes := make([]*dns.ResourceRecordSet, len(deletions))
for i, a := range additions {
adds[i] = a.(*ResourceRecordSet).impl
}
for i, d := range deletions {
deletes[i] = d.(*ResourceRecordSet).impl
}
return &Change{&dns.Change{Additions: adds, Deletions: deletes}}
}

View file

@ -0,0 +1,35 @@
/*
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 internal
// Implementation of internal/interfaces/* on top of Google Cloud DNS API.
// See https://godoc.org/google.golang.org/api/dns/v1 for details
// This facilitates stubbing out Google Cloud DNS for unit testing.
// Only the parts of the API that we use are included.
// Others can be added as needed.
import dns "google.golang.org/api/dns/v1"
type (
Project struct{ impl *dns.Project }
ProjectsGetCall struct{ impl *dns.ProjectsGetCall }
ProjectsService struct{ impl *dns.ProjectsService }
Quota struct{ impl *dns.Quota }
)

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["interfaces.go"],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//vendor:google.golang.org/api/googleapi",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,208 @@
/*
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 interfaces
import (
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Interfaces to directly mirror the Google Cloud DNS API structures.
// See https://godoc.org/google.golang.org/api/dns/v1 for details
// This facilitates stubbing out Google Cloud DNS for unit testing.
// Only the parts of the API that we use are included.
// Others can be added as needed.
type (
Change interface {
Additions() []ResourceRecordSet
Deletions() []ResourceRecordSet
// Id() string // TODO: Add as needed
// Kind() string // TODO: Add as needed
// StartTime() string // TODO: Add as needed
// Status() string // TODO: Add as needed
}
ChangesCreateCall interface {
// Context(ctx context.Context) *ChangesCreateCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) (Change, error)
// Fields(s ...googleapi.Field) *ChangesCreateCall // TODO: Add as needed
}
ChangesGetCall interface {
// Context(ctx context.Context) *ChangesGetCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) (*Change, error)
// Fields(s ...googleapi.Field) *ChangesGetCall // TODO: Add as needed
// IfNoneMatch(entityTag string) *ChangesGetCall // TODO: Add as needed
}
ChangesListCall interface {
// Context(ctx context.Context) *ChangesListCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) (*ChangesListResponse, error)
// Fields(s ...googleapi.Field) *ChangesListCall // TODO: Add as needed
// IfNoneMatch(entityTag string) *ChangesListCall // TODO: Add as needed
// MaxResults(maxResults int64) *ChangesListCall // TODO: Add as needed
// PageToken(pageToken string) *ChangesListCall // TODO: Add as needed
// Pages(ctx context.Context, f func(*ChangesListResponse) error) error // TODO: Add as needed
// SortBy(sortBy string) *ChangesListCall // TODO: Add as needed
// SortOrder(sortOrder string) *ChangesListCall // TODO: Add as needed
}
ChangesListResponse interface {
// Changes() []*Change // TODO: Add as needed
// Kind() string // TODO: Add as needed
// NextPageToken() string // TODO: Add as needed
// ServerResponse() googleapi.ServerResponse // TODO: Add as needed
// ForceSendFields() []string // TODO: Add as needed
}
ChangesService interface {
// Create(project string, managedZone string, change *Change) *ChangesCreateCall // TODO: Add as needed
Create(project string, managedZone string, change Change) ChangesCreateCall
NewChange(additions, deletions []ResourceRecordSet) Change
// Get(project string, managedZone string, changeId string) *ChangesGetCall // TODO: Add as needed
// List(project string, managedZone string) *ChangesListCall // TODO: Add as needed
}
ManagedZone interface {
// CreationTime() string // TODO: Add as needed
// Description() string // TODO: Add as needed
DnsName() string
Id() uint64
// Kind() string // TODO: Add as needed
Name() string
// NameServerSet() string // TODO: Add as needed
// NameServers() []string // TODO: Add as needed
// ServerResponse() googleapi.ServerResponse // TODO: Add as needed
// ForceSendFields() []string // TODO: Add as needed
}
ManagedZonesCreateCall interface {
// Context(ctx context.Context) *ManagedZonesCreateCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) (ManagedZone, error)
// Fields(s ...googleapi.Field) *ManagedZonesCreateCall // TODO: Add as needed
}
ManagedZonesDeleteCall interface {
// Context(ctx context.Context) *ManagedZonesDeleteCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) error
// Fields(s ...googleapi.Field) *ManagedZonesDeleteCall // TODO: Add as needed
}
ManagedZonesGetCall interface {
// Context(ctx context.Context) *ManagedZonesGetCall // TODO: Add as needed
Do(opts ...googleapi.CallOption) (ManagedZone, error)
// Fields(s ...googleapi.Field) *ManagedZonesGetCall // TODO: Add as needed
// IfNoneMatch(entityTag string) *ManagedZonesGetCall // TODO: Add as needed
}
ManagedZonesListCall interface {
// Context(ctx context.Context) *ManagedZonesListCall // TODO: Add as needed
DnsName(dnsName string) ManagedZonesListCall
Do(opts ...googleapi.CallOption) (ManagedZonesListResponse, error)
// Fields(s ...googleapi.Field) *ManagedZonesListCall // TODO: Add as needed
// IfNoneMatch(entityTag string) *ManagedZonesListCall // TODO: Add as needed
// MaxResults(maxResults int64) *ManagedZonesListCall // TODO: Add as needed
// PageToken(pageToken string) *ManagedZonesListCall // TODO: Add as needed
// Pages(ctx context.Context, f func(*ManagedZonesListResponse) error) error // TODO: Add as needed
}
ManagedZonesListResponse interface {
// Kind() string // TODO: Add as needed
// ManagedZones() []*ManagedZone // TODO: Add as needed
ManagedZones() []ManagedZone
// NextPageToken string // TODO: Add as needed
// ServerResponse() googleapi.ServerResponse // TODO: Add as needed
// ForceSendFields() []string // TODO: Add as needed
}
ManagedZonesService interface {
// NewManagedZonesService(s *Service) *ManagedZonesService // TODO: Add to service if needed
Create(project string, managedZone ManagedZone) ManagedZonesCreateCall
Delete(project string, managedZone string) ManagedZonesDeleteCall
Get(project string, managedZone string) ManagedZonesGetCall
List(project string) ManagedZonesListCall
NewManagedZone(dnsName string) ManagedZone
}
Project interface {
// Id() string // TODO: Add as needed
// Kind() string // TODO: Add as needed
// Number() uint64 // TODO: Add as needed
// Quota() *Quota // TODO: Add as needed
// ServerResponse() googleapi.ServerResponse // TODO: Add as needed
// ForceSendFields() []string // TODO: Add as needed
}
ProjectsGetCall interface {
// TODO: Add as needed
}
ProjectsService interface {
// TODO: Add as needed
}
Quota interface {
// TODO: Add as needed
}
ResourceRecordSet interface {
// Kind() string // TODO: Add as needed
Name() string
Rrdatas() []string
Ttl() int64
Type() string
// ForceSendFields []string // TODO: Add as needed
}
ResourceRecordSetsListCall interface {
// Context(ctx context.Context) *ResourceRecordSetsListCall // TODO: Add as needed
// Do(opts ...googleapi.CallOption) (*ResourceRecordSetsListResponse, error) // TODO: Add as needed
Do(opts ...googleapi.CallOption) (ResourceRecordSetsListResponse, error)
// Fields(s ...googleapi.Field) *ResourceRecordSetsListCall // TODO: Add as needed
// IfNoneMatch(entityTag string) *ResourceRecordSetsListCall // TODO: Add as needed
// MaxResults(maxResults int64) *ResourceRecordSetsListCall // TODO: Add as needed
Name(name string) ResourceRecordSetsListCall
// PageToken(pageToken string) *ResourceRecordSetsListCall // TODO: Add as needed
Type(type_ string) ResourceRecordSetsListCall
}
ResourceRecordSetsListResponse interface {
// Kind() string // TODO: Add as needed
// NextPageToken() string // TODO: Add as needed
Rrsets() []ResourceRecordSet
// ServerResponse() googleapi.ServerResponse // TODO: Add as needed
// ForceSendFields() []string // TODO: Add as needed
}
ResourceRecordSetsService interface {
// NewResourceRecordSetsService(s *Service) *ResourceRecordSetsService // TODO: add to service as needed
List(project string, managedZone string) ResourceRecordSetsListCall
NewResourceRecordSet(name string, rrdatas []string, ttl int64, type_ rrstype.RrsType) ResourceRecordSet
}
Service interface {
// BasePath() string // TODO: Add as needed
// UserAgent() string // TODO: Add as needed
Changes() ChangesService
ManagedZones() ManagedZonesService
Projects() ProjectsService
ResourceRecordSets() ResourceRecordSetsService
}
// New(client *http.Client) (*Service, error) // TODO: Add as needed
)

View file

@ -0,0 +1,39 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZone = ManagedZone{}
type ManagedZone struct{ impl *dns.ManagedZone }
func (m ManagedZone) Name() string {
return m.impl.Name
}
func (m ManagedZone) Id() uint64 {
return m.impl.Id
}
func (m ManagedZone) DnsName() string {
return m.impl.DnsName
}

View file

@ -0,0 +1,33 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesCreateCall = ManagedZonesCreateCall{}
type ManagedZonesCreateCall struct{ impl *dns.ManagedZonesCreateCall }
func (call ManagedZonesCreateCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZone, error) {
m, err := call.impl.Do(opts...)
return &ManagedZone{m}, err
}

View file

@ -0,0 +1,32 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesDeleteCall = ManagedZonesDeleteCall{}
type ManagedZonesDeleteCall struct{ impl *dns.ManagedZonesDeleteCall }
func (call ManagedZonesDeleteCall) Do(opts ...googleapi.CallOption) error {
return call.impl.Do(opts...)
}

View file

@ -0,0 +1,33 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesGetCall = ManagedZonesGetCall{}
type ManagedZonesGetCall struct{ impl *dns.ManagedZonesGetCall }
func (call ManagedZonesGetCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZone, error) {
m, err := call.impl.Do(opts...)
return &ManagedZone{m}, err
}

View file

@ -0,0 +1,38 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesListCall = &ManagedZonesListCall{}
type ManagedZonesListCall struct{ impl *dns.ManagedZonesListCall }
func (call *ManagedZonesListCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZonesListResponse, error) {
response, err := call.impl.Do(opts...)
return &ManagedZonesListResponse{response}, err
}
func (call *ManagedZonesListCall) DnsName(dnsName string) interfaces.ManagedZonesListCall {
call.impl.DnsName(dnsName)
return call
}

View file

@ -0,0 +1,35 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesListResponse = &ManagedZonesListResponse{}
type ManagedZonesListResponse struct{ impl *dns.ManagedZonesListResponse }
func (response *ManagedZonesListResponse) ManagedZones() []interfaces.ManagedZone {
zones := make([]interfaces.ManagedZone, len(response.impl.ManagedZones))
for i, z := range response.impl.ManagedZones {
zones[i] = &ManagedZone{z}
}
return zones
}

View file

@ -0,0 +1,51 @@
/*
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 internal
import (
"strings"
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
"k8s.io/kubernetes/pkg/util/uuid"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesService = &ManagedZonesService{}
type ManagedZonesService struct{ impl *dns.ManagedZonesService }
func (m *ManagedZonesService) Create(project string, managedzone interfaces.ManagedZone) interfaces.ManagedZonesCreateCall {
return &ManagedZonesCreateCall{m.impl.Create(project, managedzone.(*ManagedZone).impl)}
}
func (m *ManagedZonesService) Delete(project, managedZone string) interfaces.ManagedZonesDeleteCall {
return &ManagedZonesDeleteCall{m.impl.Delete(project, managedZone)}
}
func (m *ManagedZonesService) Get(project, managedZone string) interfaces.ManagedZonesGetCall {
return &ManagedZonesGetCall{m.impl.Get(project, managedZone)}
}
func (m *ManagedZonesService) List(project string) interfaces.ManagedZonesListCall {
return &ManagedZonesListCall{m.impl.List(project)}
}
func (m *ManagedZonesService) NewManagedZone(dnsName string) interfaces.ManagedZone {
name := "x" + strings.Replace(string(uuid.NewUUID()), "-", "", -1)[0:30] // Unique name, strip out the "-" chars to shorten it, start with a lower case alpha, and truncate to Cloud DNS 32 character limit
return &ManagedZone{impl: &dns.ManagedZone{Name: name, Description: "Kubernetes Federated Service", DnsName: dnsName}}
}

View file

@ -0,0 +1,32 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSet = ResourceRecordSet{}
type ResourceRecordSet struct{ impl *dns.ResourceRecordSet }
func (r ResourceRecordSet) Name() string { return r.impl.Name }
func (r ResourceRecordSet) Rrdatas() []string { return r.impl.Rrdatas }
func (r ResourceRecordSet) Ttl() int64 { return r.impl.Ttl }
func (r ResourceRecordSet) Type() string { return r.impl.Type }

View file

@ -0,0 +1,45 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsListCall = &ResourceRecordSetsListCall{}
type ResourceRecordSetsListCall struct {
impl *dns.ResourceRecordSetsListCall
}
func (call *ResourceRecordSetsListCall) Do(opts ...googleapi.CallOption) (interfaces.ResourceRecordSetsListResponse, error) {
response, err := call.impl.Do(opts...)
return &ResourceRecordSetsListResponse{response}, err
}
func (call *ResourceRecordSetsListCall) Name(name string) interfaces.ResourceRecordSetsListCall {
call.impl.Name(name)
return call
}
func (call *ResourceRecordSetsListCall) Type(type_ string) interfaces.ResourceRecordSetsListCall {
call.impl.Type(type_)
return call
}

View file

@ -0,0 +1,38 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsListResponse = &ResourceRecordSetsListResponse{}
type ResourceRecordSetsListResponse struct {
impl *dns.ResourceRecordSetsListResponse
}
func (response *ResourceRecordSetsListResponse) Rrsets() []interfaces.ResourceRecordSet {
rrsets := make([]interfaces.ResourceRecordSet, len(response.impl.Rrsets))
for i, rrset := range response.impl.Rrsets {
rrsets[i] = &ResourceRecordSet{rrset}
}
return rrsets
}

View file

@ -0,0 +1,39 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsService = &ResourceRecordSetsService{}
type ResourceRecordSetsService struct {
impl *dns.ResourceRecordSetsService
}
func (service ResourceRecordSetsService) List(project string, managedZone string) interfaces.ResourceRecordSetsListCall {
return &ResourceRecordSetsListCall{service.impl.List(project, managedZone)}
}
func (service ResourceRecordSetsService) NewResourceRecordSet(name string, rrdatas []string, ttl int64, type_ rrstype.RrsType) interfaces.ResourceRecordSet {
rrset := dns.ResourceRecordSet{Name: name, Rrdatas: rrdatas, Ttl: ttl, Type: string(type_)}
return &ResourceRecordSet{&rrset}
}

View file

@ -0,0 +1,49 @@
/*
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 internal
import (
dns "google.golang.org/api/dns/v1"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.Service = &Service{}
type Service struct {
impl *dns.Service
}
func NewService(service *dns.Service) *Service {
return &Service{service}
}
func (s *Service) Changes() interfaces.ChangesService {
return &ChangesService{s.impl.Changes}
}
func (s *Service) ManagedZones() interfaces.ManagedZonesService {
return &ManagedZonesService{s.impl.ManagedZones}
}
func (s *Service) Projects() interfaces.ProjectsService {
return &ProjectsService{s.impl.Projects}
}
func (s *Service) ResourceRecordSets() interfaces.ResourceRecordSetsService {
return &ResourceRecordSetsService{s.impl.ResourceRecordSets}
}

View file

@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"change.go",
"changes_create_call.go",
"changes_service.go",
"clouddns.go",
"managed_zone.go",
"managed_zone_create_call.go",
"managed_zones_delete_call.go",
"managed_zones_get_call.go",
"managed_zones_list_call.go",
"managed_zones_list_response.go",
"managed_zones_service.go",
"rrset.go",
"rrsets_list_call.go",
"rrsets_list_response.go",
"rrsets_service.go",
"service.go",
],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
"//vendor:google.golang.org/api/dns/v1",
"//vendor:google.golang.org/api/googleapi",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,36 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.Change = &Change{}
type Change struct {
Service *ChangesService
Additions_ []interfaces.ResourceRecordSet
Deletions_ []interfaces.ResourceRecordSet
}
func (c *Change) Additions() (rrsets []interfaces.ResourceRecordSet) {
return c.Additions_
}
func (c *Change) Deletions() (rrsets []interfaces.ResourceRecordSet) {
return c.Deletions_
}

View file

@ -0,0 +1,67 @@
/*
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 stubs
import (
"fmt"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ChangesCreateCall = ChangesCreateCall{}
type ChangesCreateCall struct {
Service *ChangesService
Project string
Zone string
Change interfaces.Change
Error error // Use this to over-ride response if necessary
}
func hashKey(set interfaces.ResourceRecordSet) string {
return fmt.Sprintf("%s-%d-%s", set.Name(), set.Ttl(), string(set.Type()))
}
func (c ChangesCreateCall) Do(opts ...googleapi.CallOption) (interfaces.Change, error) {
if c.Error != nil {
return nil, c.Error
}
zone := (c.Service.Service.ManagedZones_.Impl[c.Project][c.Zone]).(*ManagedZone)
rrsets := map[string]ResourceRecordSet{} // compute the new state
for _, set := range zone.Rrsets {
rrsets[hashKey(set)] = set
}
for _, del := range c.Change.Deletions() {
if _, found := rrsets[hashKey(del)]; !found {
return nil, fmt.Errorf("Attempt to delete non-existent rrset %v", del)
}
delete(rrsets, hashKey(del))
}
for _, add := range c.Change.Additions() {
if _, found := rrsets[hashKey(add)]; found {
return nil, fmt.Errorf("Attempt to insert duplicate rrset %v", add)
}
rrsets[hashKey(add)] = add.(ResourceRecordSet)
}
zone.Rrsets = []ResourceRecordSet{}
for _, rrset := range rrsets {
zone.Rrsets = append(zone.Rrsets, rrset)
}
return c.Change, nil
}

View file

@ -0,0 +1,34 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ChangesService = &ChangesService{}
type ChangesService struct {
Service *Service
}
func (c *ChangesService) Create(project string, managedZone string, change interfaces.Change) interfaces.ChangesCreateCall {
return &ChangesCreateCall{c, project, managedZone, change, nil}
}
func (c *ChangesService) NewChange(additions, deletions []interfaces.ResourceRecordSet) interfaces.Change {
return &Change{c, additions, deletions}
}

View file

@ -0,0 +1,33 @@
/*
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 stubs
// Implementation of internal/interfaces/* on top of Google Cloud DNS API.
// See https://godoc.org/google.golang.org/api/dns/v1 for details
// This facilitates stubbing out Google Cloud DNS for unit testing.
// Only the parts of the API that we use are included.
// Others can be added as needed.
import dns "google.golang.org/api/dns/v1"
type (
// TODO: We don't need these yet, so they remain unimplemented. Add later as required.
Project struct{ impl *dns.Project }
ProjectsGetCall struct{ impl *dns.ProjectsGetCall }
ProjectsService struct{ impl *dns.ProjectsService }
Quota struct{ impl *dns.Quota }
)

View file

@ -0,0 +1,41 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ManagedZone = ManagedZone{}
type ManagedZone struct {
Service *ManagedZonesService
Name_ string
Id_ uint64
Rrsets []ResourceRecordSet
}
func (m ManagedZone) Name() string {
return m.Name_
}
func (m ManagedZone) Id() uint64 {
return m.Id_
}
func (m ManagedZone) DnsName() string {
return m.Name_ // Don't bother storing a separate DNS name
}

View file

@ -0,0 +1,52 @@
/*
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 stubs
import (
"fmt"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesCreateCall = ManagedZonesCreateCall{}
type ManagedZonesCreateCall struct {
Error *error // Use to override response for testing
Service *ManagedZonesService
Project string
ManagedZone interfaces.ManagedZone
}
func (call ManagedZonesCreateCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZone, error) {
if call.Error != nil {
return nil, *call.Error
}
if call.Service.Impl[call.Project][call.ManagedZone.DnsName()] != nil {
return nil, fmt.Errorf("Error - attempt to create duplicate zone %s in project %s.",
call.ManagedZone.DnsName(), call.Project)
}
if call.Service.Impl == nil {
call.Service.Impl = map[string]map[string]interfaces.ManagedZone{}
}
if call.Service.Impl[call.Project] == nil {
call.Service.Impl[call.Project] = map[string]interfaces.ManagedZone{}
}
call.Service.Impl[call.Project][call.ManagedZone.DnsName()] = call.ManagedZone
return call.ManagedZone, nil
}

View file

@ -0,0 +1,53 @@
/*
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 stubs
import (
"fmt"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesDeleteCall = ManagedZonesDeleteCall{}
type ManagedZonesDeleteCall struct {
Service *ManagedZonesService
Project string
ZoneName string
Error *error // Use this to override response for testing if required
}
func (call ManagedZonesDeleteCall) Do(opts ...googleapi.CallOption) error {
if call.Error != nil { // Return the override value
return *call.Error
} else { // Just try to delete it from the in-memory array.
project, ok := call.Service.Impl[call.Project]
if ok {
zone, ok := project[call.ZoneName]
if ok {
delete(project, zone.Name())
return nil
} else {
return fmt.Errorf("Failed to find zone %s in project %s to delete it", call.ZoneName, call.Project)
}
} else {
return fmt.Errorf("Failed to find project %s to delete zone %s from it", call.Project, call.ZoneName)
}
}
}

View file

@ -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 stubs
import (
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesGetCall = ManagedZonesGetCall{}
type ManagedZonesGetCall struct {
Service *ManagedZonesService
Project string
ZoneName string
Response interfaces.ManagedZone // Use this to override response if required
Error *error // Use this to override response if required
DnsName_ string
}
func (call ManagedZonesGetCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZone, error) {
if call.Response != nil {
return call.Response, *call.Error
} else {
return call.Service.Impl[call.Project][call.ZoneName], nil
}
}

View file

@ -0,0 +1,59 @@
/*
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 stubs
import (
"fmt"
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ManagedZonesListCall = &ManagedZonesListCall{}
type ManagedZonesListCall struct {
Service *ManagedZonesService
Project string
Response *interfaces.ManagedZonesListResponse // Use this to override response if required
Error *error // Use this to override response if required
DnsName_ string
}
func (call *ManagedZonesListCall) Do(opts ...googleapi.CallOption) (interfaces.ManagedZonesListResponse, error) {
if call.Response != nil {
return *call.Response, *call.Error
} else {
proj, projectFound := call.Service.Impl[call.Project]
if !projectFound {
return nil, fmt.Errorf("Project %s not found.", call.Project)
}
if call.DnsName_ != "" {
return &ManagedZonesListResponse{[]interfaces.ManagedZone{proj[call.DnsName_]}}, nil
}
list := []interfaces.ManagedZone{}
for _, zone := range proj {
list = append(list, zone)
}
return &ManagedZonesListResponse{list}, nil
}
}
func (call *ManagedZonesListCall) DnsName(dnsName string) interfaces.ManagedZonesListCall {
call.DnsName_ = dnsName
return call
}

View file

@ -0,0 +1,28 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ManagedZonesListResponse = &ManagedZonesListResponse{}
type ManagedZonesListResponse struct{ ManagedZones_ []interfaces.ManagedZone }
func (response *ManagedZonesListResponse) ManagedZones() []interfaces.ManagedZone {
return response.ManagedZones_
}

View file

@ -0,0 +1,46 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ManagedZonesService = &ManagedZonesService{}
type ManagedZonesService struct {
Impl map[string]map[string]interfaces.ManagedZone
}
func (m *ManagedZonesService) Create(project string, managedzone interfaces.ManagedZone) interfaces.ManagedZonesCreateCall {
return &ManagedZonesCreateCall{nil, m, project, managedzone.(*ManagedZone)}
}
func (m *ManagedZonesService) Delete(project string, managedZone string) interfaces.ManagedZonesDeleteCall {
return &ManagedZonesDeleteCall{m, project, managedZone, nil}
}
func (m *ManagedZonesService) Get(project string, managedZone string) interfaces.ManagedZonesGetCall {
return &ManagedZonesGetCall{m, project, managedZone, nil, nil, ""}
}
func (m *ManagedZonesService) List(project string) interfaces.ManagedZonesListCall {
return &ManagedZonesListCall{m, project, nil, nil, ""}
}
func (m *ManagedZonesService) NewManagedZone(dnsName string) interfaces.ManagedZone {
return &ManagedZone{Service: m, Name_: dnsName}
}

View file

@ -0,0 +1,34 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSet = ResourceRecordSet{}
type ResourceRecordSet struct {
Name_ string
Rrdatas_ []string
Ttl_ int64
Type_ string
}
func (r ResourceRecordSet) Name() string { return r.Name_ }
func (r ResourceRecordSet) Rrdatas() []string { return r.Rrdatas_ }
func (r ResourceRecordSet) Ttl() int64 { return r.Ttl_ }
func (r ResourceRecordSet) Type() string { return r.Type_ }

View file

@ -0,0 +1,46 @@
/*
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 stubs
import (
"google.golang.org/api/googleapi"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsListCall = &ResourceRecordSetsListCall{}
type ResourceRecordSetsListCall struct {
Response_ *ResourceRecordSetsListResponse
Err_ error
Name_ string
Type_ string
}
func (call *ResourceRecordSetsListCall) Do(opts ...googleapi.CallOption) (interfaces.ResourceRecordSetsListResponse, error) {
return call.Response_, call.Err_
}
func (call *ResourceRecordSetsListCall) Name(name string) interfaces.ResourceRecordSetsListCall {
call.Name_ = name
return call
}
func (call *ResourceRecordSetsListCall) Type(type_ string) interfaces.ResourceRecordSetsListCall {
call.Type_ = type_
return call
}

View file

@ -0,0 +1,30 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsListResponse = &ResourceRecordSetsListResponse{}
type ResourceRecordSetsListResponse struct {
impl []interfaces.ResourceRecordSet
}
func (response *ResourceRecordSetsListResponse) Rrsets() []interfaces.ResourceRecordSet {
return response.impl
}

View file

@ -0,0 +1,59 @@
/*
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 stubs
import (
"fmt"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ interfaces.ResourceRecordSetsService = &ResourceRecordSetsService{}
type ResourceRecordSetsService struct {
Service *Service
ListCall interfaces.ResourceRecordSetsListCall // Use to override response if required for testing
}
func (s ResourceRecordSetsService) List(project string, managedZone string) interfaces.ResourceRecordSetsListCall {
if s.ListCall != nil {
return s.ListCall
}
p := s.Service.ManagedZones_.Impl[project]
if p == nil {
return &ResourceRecordSetsListCall{Err_: fmt.Errorf("Project not found: %s", project)}
}
z := s.Service.ManagedZones_.Impl[project][managedZone]
if z == nil {
return &ResourceRecordSetsListCall{
Err_: fmt.Errorf("Zone %s not found in project %s", managedZone, project),
}
}
zone := s.Service.ManagedZones_.Impl[project][managedZone].(*ManagedZone)
response := &ResourceRecordSetsListResponse{}
for _, set := range zone.Rrsets {
response.impl = append(response.impl, set)
}
return &ResourceRecordSetsListCall{Response_: response}
}
func (service ResourceRecordSetsService) NewResourceRecordSet(name string, rrdatas []string, ttl int64, type_ rrstype.RrsType) interfaces.ResourceRecordSet {
rrset := ResourceRecordSet{Name_: name, Rrdatas_: rrdatas, Ttl_: ttl, Type_: string(type_)}
return rrset
}

View file

@ -0,0 +1,54 @@
/*
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 stubs
import "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
// Compile time check for interface adeherence
var _ interfaces.Service = &Service{}
type Service struct {
Changes_ *ChangesService
ManagedZones_ *ManagedZonesService
Projects_ *ProjectsService
Rrsets_ *ResourceRecordSetsService
}
func NewService() *Service {
s := &Service{}
s.Changes_ = &ChangesService{s}
s.ManagedZones_ = &ManagedZonesService{}
s.Projects_ = &ProjectsService{}
s.Rrsets_ = &ResourceRecordSetsService{s, nil}
return s
}
func (s *Service) Changes() interfaces.ChangesService {
return s.Changes_
}
func (s *Service) ManagedZones() interfaces.ManagedZonesService {
return s.ManagedZones_
}
func (s *Service) Projects() interfaces.ProjectsService {
return s.Projects_
}
func (s *Service) ResourceRecordSets() interfaces.ResourceRecordSetsService {
return s.Rrsets_
}

View file

@ -0,0 +1,74 @@
/*
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 clouddns
import (
"fmt"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordChangeset = &ResourceRecordChangeset{}
type ResourceRecordChangeset struct {
rrsets *ResourceRecordSets
additions []dnsprovider.ResourceRecordSet
removals []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) Apply() error {
rrsets := c.rrsets
service := rrsets.zone.zones.interface_.service.Changes()
var additions []interfaces.ResourceRecordSet
for _, r := range c.additions {
additions = append(additions, r.(ResourceRecordSet).impl)
}
var deletions []interfaces.ResourceRecordSet
for _, r := range c.removals {
deletions = append(deletions, r.(ResourceRecordSet).impl)
}
change := service.NewChange(additions, deletions)
newChange, err := service.Create(rrsets.project(), rrsets.zone.impl.Name(), change).Do()
if err != nil {
return err
}
newAdditions := newChange.Additions()
if len(newAdditions) != len(additions) {
return fmt.Errorf("Internal error when adding resource record set. Call succeeded but number of records returned is incorrect. Records sent=%d, records returned=%d, additions:%v", len(additions), len(newAdditions), c.additions)
}
newDeletions := newChange.Deletions()
if len(newDeletions) != len(deletions) {
return fmt.Errorf("Internal error when deleting resource record set. Call succeeded but number of records returned is incorrect. Records sent=%d, records returned=%d, deletions:%v", len(deletions), len(newDeletions), c.removals)
}
return nil
}

View file

@ -0,0 +1,53 @@
/*
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 clouddns
import (
"fmt"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSet = ResourceRecordSet{}
type ResourceRecordSet struct {
impl interfaces.ResourceRecordSet
rrsets *ResourceRecordSets
}
func (rrset ResourceRecordSet) String() string {
return fmt.Sprintf("<(clouddns) %q type=%s rrdatas=%q ttl=%v>", rrset.Name(), rrset.Type(), rrset.Rrdatas(), rrset.Ttl())
}
func (rrset ResourceRecordSet) Name() string {
return rrset.impl.Name()
}
func (rrset ResourceRecordSet) Rrdatas() []string {
return rrset.impl.Rrdatas()
}
func (rrset ResourceRecordSet) Ttl() int64 {
return rrset.impl.Ttl()
}
func (rrset ResourceRecordSet) Type() rrstype.RrsType {
return rrstype.RrsType(rrset.impl.Type())
}

View file

@ -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 clouddns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
// Compile time check for interface adherence
var _ dnsprovider.ResourceRecordSets = ResourceRecordSets{}
type ResourceRecordSets struct {
zone *Zone
impl interfaces.ResourceRecordSetsService
}
func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
response, err := rrsets.impl.List(rrsets.project(), rrsets.zone.impl.Name()).Do()
if err != nil {
return nil, err
}
list := make([]dnsprovider.ResourceRecordSet, len(response.Rrsets()))
for i, rrset := range response.Rrsets() {
list[i] = ResourceRecordSet{rrset, &rrsets}
}
return list, nil
}
func (rrsets ResourceRecordSets) Get(name string) (dnsprovider.ResourceRecordSet, error) {
var newRrset dnsprovider.ResourceRecordSet
rrsetList, err := rrsets.List()
if err != nil {
return nil, err
}
for _, rrset := range rrsetList {
if rrset.Name() == name {
newRrset = rrset
break
}
}
return newRrset, nil
}
func (r ResourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset {
return &ResourceRecordChangeset{
rrsets: &r,
}
}
func (r ResourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet {
return ResourceRecordSet{r.impl.NewResourceRecordSet(name, rrdatas, ttl, rrstype), &r}
}
func (rrsets ResourceRecordSets) project() string {
return rrsets.zone.project()
}

View file

@ -0,0 +1,48 @@
/*
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 clouddns
import (
"strconv"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adeherence
var _ dnsprovider.Zone = &Zone{}
type Zone struct {
impl interfaces.ManagedZone
zones *Zones
}
func (zone *Zone) Name() string {
return zone.impl.DnsName()
}
func (zone *Zone) ID() string {
return strconv.FormatUint(zone.impl.Id(), 10)
}
func (zone *Zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) {
return &ResourceRecordSets{zone, zone.zones.interface_.service.ResourceRecordSets()}, true
}
func (zone Zone) project() string {
return zone.zones.project()
}

View file

@ -0,0 +1,68 @@
/*
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 clouddns
import (
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces"
)
// Compile time check for interface adeherence
var _ dnsprovider.Zones = Zones{}
type Zones struct {
impl interfaces.ManagedZonesService
interface_ *Interface
}
func (zones Zones) List() ([]dnsprovider.Zone, error) {
response, err := zones.impl.List(zones.project()).Do()
if err != nil {
return nil, err
}
managedZones := response.ManagedZones()
zoneList := make([]dnsprovider.Zone, len(managedZones))
for i, zone := range managedZones {
zoneList[i] = &Zone{zone, &zones}
}
return zoneList, nil
}
func (zones Zones) Add(zone dnsprovider.Zone) (dnsprovider.Zone, error) {
managedZone := zones.impl.NewManagedZone(zone.Name())
response, err := zones.impl.Create(zones.project(), managedZone).Do()
if err != nil {
return nil, err
}
return &Zone{response, &zones}, nil
}
func (zones Zones) Remove(zone dnsprovider.Zone) error {
if err := zones.impl.Delete(zones.project(), zone.(*Zone).impl.Name()).Do(); err != nil {
return err
}
return nil
}
func (zones Zones) New(name string) (dnsprovider.Zone, error) {
managedZone := zones.impl.NewManagedZone(name)
return &Zone{managedZone, &zones}, nil
}
func (zones Zones) project() string {
return zones.interface_.project()
}

View file

@ -0,0 +1,27 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["rrstype.go"],
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,28 @@
/*
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 rrstype
type (
RrsType string
)
const (
A = RrsType("A")
AAAA = RrsType("AAAA")
CNAME = RrsType("CNAME")
// TODO: Add other types as required
)

View file

@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["commontests.go"],
tags = ["automanaged"],
deps = [
"//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,189 @@
/*
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 tests
import (
"reflect"
"testing"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
/* CommonTestResourceRecordSetsReplace verifies that replacing an RRS works */
func CommonTestResourceRecordSetsReplace(t *testing.T, zone dnsprovider.Zone) {
rrsets, _ := zone.ResourceRecordSets()
sets := rrs(t, zone)
rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
// Replace the record (change ttl and rrdatas)
newRrset := rrsets.New("alpha.test.com", []string{"8.8.8.8"}, 80, rrstype.A)
err := sets.StartChangeset().Add(newRrset).Remove(rrset).Apply()
if err != nil {
t.Errorf("Failed to replace resource record set %v -> %v: %v", rrset, newRrset, err)
} else {
t.Logf("Correctly replaced resource record %v -> %v", rrset, newRrset)
}
defer sets.StartChangeset().Remove(newRrset).Apply()
// Check that the record was updated
assertHasRecord(t, sets, newRrset)
}
/* CommonTestResourceRecordSetsReplaceAll verifies that we can remove an RRS and create one with a different name*/
func CommonTestResourceRecordSetsReplaceAll(t *testing.T, zone dnsprovider.Zone) {
rrsets, _ := zone.ResourceRecordSets()
sets := rrs(t, zone)
rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
newRrset := rrsets.New("beta.test.com", []string{"8.8.8.8"}, 80, rrstype.A)
// Try to add it again, and verify that the call fails.
err := sets.StartChangeset().Add(newRrset).Remove(rrset).Apply()
if err != nil {
t.Errorf("Failed to replace resource record set %v -> %v: %v", rrset, newRrset, err)
} else {
defer sets.StartChangeset().Remove(newRrset).Apply()
t.Logf("Correctly replaced resource record %v -> %v", rrset, newRrset)
}
// Check that it was updated
assertHasRecord(t, sets, newRrset)
assertNotHasRecord(t, sets, rrset.Name(), rrset.Type())
}
/* CommonTestResourceRecordSetsHonorsType verifies that we can add records of the same name but different types */
func CommonTestResourceRecordSetsDifferentTypes(t *testing.T, zone dnsprovider.Zone) {
rrsets, _ := zone.ResourceRecordSets()
sets := rrs(t, zone)
rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A)
addRrsetOrFail(t, sets, rrset)
defer sets.StartChangeset().Remove(rrset).Apply()
t.Logf("Successfully added resource record set: %v", rrset)
aaaaRrset := rrsets.New("alpha.test.com", []string{"2001:4860:4860::8888"}, 80, rrstype.AAAA)
// Add the resource with the same name but different type
err := sets.StartChangeset().Add(aaaaRrset).Apply()
if err != nil {
t.Errorf("Failed to add resource record set %v: %v", aaaaRrset, err)
}
defer sets.StartChangeset().Remove(aaaaRrset).Apply()
// Check that both records exist
assertHasRecord(t, sets, aaaaRrset)
assertHasRecord(t, sets, rrset)
}
/* rrs returns the ResourceRecordSets interface for a given zone */
func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) {
rrsets, supported := zone.ResourceRecordSets()
if !supported {
t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone)
return r
}
return rrsets
}
func getRrOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, name string) dnsprovider.ResourceRecordSet {
rrset, err := rrsets.Get(name)
if err != nil {
t.Fatalf("Failed to get recordset: %v", err)
} else if rrset == nil {
t.Logf("Did not Get recordset: %v", name)
} else {
t.Logf("Got recordset: %v", rrset.Name())
}
return rrset
}
// assertHasRecord tests that rrsets has a record equivalent to rrset
func assertHasRecord(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) {
var found dnsprovider.ResourceRecordSet
rrs, err := rrsets.List()
if err != nil {
if err.Error() == "OperationNotSupported" {
found = getRrOrFail(t, rrsets, rrset.Name())
} else {
t.Fatalf("Failed to list recordsets: %v", err)
}
} else {
if len(rrs) < 0 {
t.Fatalf("Record set length=%d, expected >=0", len(rrs))
} else {
t.Logf("Got %d recordsets: %v", len(rrs), rrs)
}
for _, r := range rrs {
if r.Name() != rrset.Name() || r.Type() != rrset.Type() {
continue
}
if found != nil {
t.Errorf("found duplicate resource record set: %q and %q", r, found)
}
found = r
}
}
if found == nil {
t.Errorf("resource record set %v not found", rrset)
} else {
assertEquivalent(t, found, rrset)
}
}
// assertNotHasRecord tests that rrsets does not have a record matching name and type
func assertNotHasRecord(t *testing.T, rrsets dnsprovider.ResourceRecordSets, name string, rrstype rrstype.RrsType) {
found := getRrOrFail(t, rrsets, name)
if found != nil {
t.Errorf("resource record set found unexpectedly: %v", found)
}
}
// assertEquivalent tests that l is equal to r, for the methods in ResourceRecordSet
func assertEquivalent(t *testing.T, l, r dnsprovider.ResourceRecordSet) {
if l.Name() != r.Name() {
t.Errorf("resource record sets not equal %v vs %v", l, r)
}
if l.Type() != r.Type() {
t.Errorf("resource record sets not equal %v vs %v", l, r)
}
if l.Ttl() != r.Ttl() {
t.Errorf("resource record sets not equal %v vs %v", l, r)
}
if !reflect.DeepEqual(l.Rrdatas(), r.Rrdatas()) {
t.Errorf("resource record sets not equal %v vs %v", l, r)
}
}
func addRrsetOrFail(t *testing.T, rrsets dnsprovider.ResourceRecordSets, rrset dnsprovider.ResourceRecordSet) {
err := rrsets.StartChangeset().Add(rrset).Apply()
if err != nil {
t.Fatalf("Failed to add recordsets: %v", err)
}
}