mirror of https://github.com/kubernetes/kops.git
Add copy of k8s.io/kubernetes/federation/pkg/dnsprovider/
This commit is contained in:
parent
db09337e42
commit
29cd6c633e
|
@ -0,0 +1,9 @@
|
||||||
|
This is a (partial) copy of federation/pkg/dnsprovider from k8s.io/kubernetes.
|
||||||
|
|
||||||
|
Changes made here should be submitted upstream.
|
||||||
|
|
||||||
|
Reasons for doing this:
|
||||||
|
|
||||||
|
* Ability to fix bugs quickly
|
||||||
|
* We want to stop vendoring k8s.io/kubernetes
|
||||||
|
* The federation code is likely to move somewhere else in future
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
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 list with the name in the Zone.
|
||||||
|
// This is a list because there might be multiple records of different
|
||||||
|
// types for a given name. If the named resource record sets do not
|
||||||
|
// exist, but no error occurred, the returned record set will be empty
|
||||||
|
// and error will be 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
|
||||||
|
// Zone returns the parent zone
|
||||||
|
Zone() Zone
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// Upsert adds an "create or update" operation for the ResourceRecordSet in the Zone to the changeset
|
||||||
|
// Note: the implementation may translate this into a Remove followed by an Add operation.
|
||||||
|
// If you have the pre-image, it will likely be more efficient to call Remove and Add.
|
||||||
|
Upsert(ResourceRecordSet) ResourceRecordChangeset
|
||||||
|
// Apply applies the accumulated operations to the Zone.
|
||||||
|
Apply() error
|
||||||
|
// IsEmpty returns true if there are no accumulated operations.
|
||||||
|
IsEmpty() bool
|
||||||
|
// ResourceRecordSets returns the parent ResourceRecordSets
|
||||||
|
ResourceRecordSets() ResourceRecordSets
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// route53 is the implementation of pkg/dnsprovider interface for AWS Route53
|
||||||
|
package route53
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/route53"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/kubernetes/federation/pkg/dnsprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProviderName = "aws-route53"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dnsprovider.RegisterDnsProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) {
|
||||||
|
return newRoute53(config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// route53HandlerLogger is a request handler for aws-sdk-go that logs route53 requests
|
||||||
|
func route53HandlerLogger(req *request.Request) {
|
||||||
|
service := req.ClientInfo.ServiceName
|
||||||
|
|
||||||
|
name := "?"
|
||||||
|
if req.Operation != nil {
|
||||||
|
name = req.Operation.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(4).Infof("AWS request: %s %s", service, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
awsConfig := aws.NewConfig()
|
||||||
|
|
||||||
|
// This avoids a confusing error message when we fail to get credentials
|
||||||
|
// e.g. https://github.com/kubernetes/kops/issues/605
|
||||||
|
awsConfig = awsConfig.WithCredentialsChainVerboseErrors(true)
|
||||||
|
|
||||||
|
svc := route53.New(session.New(), awsConfig)
|
||||||
|
|
||||||
|
// Add our handler that will log requests
|
||||||
|
svc.Handlers.Sign.PushFrontNamed(request.NamedHandler{
|
||||||
|
Name: "k8s/logger",
|
||||||
|
Fn: route53HandlerLogger,
|
||||||
|
})
|
||||||
|
|
||||||
|
return New(svc), nil
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TestResourceRecordSetsDifferentTypes 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)
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/route53"
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"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
|
||||||
|
upserts []dnsprovider.ResourceRecordSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.additions = append(c.additions, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.removals = append(c.removals, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.upserts = append(c.upserts, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, upsert := range c.upserts {
|
||||||
|
change := buildChange(route53.ChangeActionUpsert, upsert)
|
||||||
|
changes = append(changes, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if glog.V(8) {
|
||||||
|
var sb bytes.Buffer
|
||||||
|
for _, change := range changes {
|
||||||
|
sb.WriteString(fmt.Sprintf("\t%s %s %s\n", aws.StringValue(change.Action), aws.StringValue(change.ResourceRecordSet.Type), aws.StringValue(change.ResourceRecordSet.Name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(8).Infof("Route53 Changeset:\n%s", sb.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) IsEmpty() bool {
|
||||||
|
return len(c.removals) == 0 && len(c.additions) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRecordSets returns the parent ResourceRecordSets
|
||||||
|
func (c *ResourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets {
|
||||||
|
return c.rrsets
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
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/aws"
|
||||||
|
"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 aws.StringValue(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] = aws.StringValue(record.Value)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rrset ResourceRecordSet) Ttl() int64 {
|
||||||
|
return aws.Int64Value(rrset.impl.TTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rrset ResourceRecordSet) Type() rrstype.RrsType {
|
||||||
|
return rrstype.RrsType(aws.StringValue(rrset.impl.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route53ResourceRecordSet returns the route53 ResourceRecordSet object for the ResourceRecordSet
|
||||||
|
// This is a "back door" that allows for limited access to the ResourceRecordSet,
|
||||||
|
// without having to requery it, so that we can expose AWS specific functionality.
|
||||||
|
// Using this method should be avoided where possible; instead prefer to add functionality
|
||||||
|
// to the cross-provider ResourceRecordSet interface.
|
||||||
|
func (rrset ResourceRecordSet) Route53ResourceRecordSet() *route53.ResourceRecordSet {
|
||||||
|
return rrset.impl
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
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) {
|
||||||
|
// This list implementation is very similar to the one implemented in
|
||||||
|
// the List() method above, but it restricts the retrieved list to
|
||||||
|
// the records whose name match the given `name`.
|
||||||
|
input := route53.ListResourceRecordSetsInput{
|
||||||
|
HostedZoneId: rrsets.zone.impl.Id,
|
||||||
|
StartRecordName: aws.String(name),
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []dnsprovider.ResourceRecordSet
|
||||||
|
err := rrsets.zone.zones.interface_.service.ListResourceRecordSetsPages(&input, func(page *route53.ListResourceRecordSetsOutput, lastPage bool) bool {
|
||||||
|
for _, rrset := range page.ResourceRecordSets {
|
||||||
|
if aws.StringValue(rrset.Name) != name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
list = append(list, &ResourceRecordSet{rrset, &rrsets})
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the parent zone
|
||||||
|
func (rrset ResourceRecordSets) Zone() dnsprovider.Zone {
|
||||||
|
return rrset.zone
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route53HostedZone returns the route53 HostedZone object for the zone.
|
||||||
|
// This is a "back door" that allows for limited access to the HostedZone,
|
||||||
|
// without having to requery it, so that we can expose AWS specific functionality.
|
||||||
|
// Using this method should be avoided where possible; instead prefer to add functionality
|
||||||
|
// to the cross-provider Zone interface.
|
||||||
|
func (zone *Zone) Route53HostedZone() *route53.HostedZone {
|
||||||
|
return zone.impl
|
||||||
|
}
|
|
@ -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/apimachinery/pkg/util/uuid"
|
||||||
|
"k8s.io/kubernetes/federation/pkg/dnsprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
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"`
|
||||||
|
CoreDNSEndpoints string `gcfg:"coredns-endpoints"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
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 {
|
||||||
|
rrsetList, err := rrsets.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get recordset: %v", err)
|
||||||
|
} else if len(rrsetList) == 0 {
|
||||||
|
t.Logf("Did not Get recordset: %v", name)
|
||||||
|
} else {
|
||||||
|
t.Logf("Got recordsets: %v", rrsetList)
|
||||||
|
}
|
||||||
|
return rrsetList
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if sets.Zone().ID() != zone.ID() {
|
||||||
|
t.Errorf("Zone for rrset does not match expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TestResourceRecordSetsDifferentTypes verifies that we can add records with same name, but different types */
|
||||||
|
func TestResourceRecordSetsDifferentTypes(t *testing.T) {
|
||||||
|
zone := firstZone(t)
|
||||||
|
tests.CommonTestResourceRecordSetsDifferentTypes(t, zone)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
"hash/fnv"
|
||||||
|
|
||||||
|
etcdc "github.com/coreos/etcd/client"
|
||||||
|
dnsmsg "github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"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")
|
||||||
|
UPSERT = ChangeSetType("UPSERT")
|
||||||
|
)
|
||||||
|
|
||||||
|
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) IsEmpty() bool {
|
||||||
|
return len(c.changeset) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.changeset = append(c.changeset, ChangeSet{cstype: UPSERT, 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, UPSERT:
|
||||||
|
checkNotExists := changeset.cstype == ADDITION
|
||||||
|
|
||||||
|
// TODO: I think the semantics of the other providers are different; they operate at the record level, not the individual rrdata level
|
||||||
|
// In other words: we should insert/replace all the records for the key
|
||||||
|
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)
|
||||||
|
|
||||||
|
if checkNotExists {
|
||||||
|
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:
|
||||||
|
// TODO: I think the semantics of the other providers are different; they operate at the record level, not the individual rrdata level
|
||||||
|
// In other words: we should delete all the records for the key, only if it matches exactly
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRecordSets returns the parent ResourceRecordSets
|
||||||
|
func (c *ResourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets {
|
||||||
|
return c.rrsets
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []dnsprovider.ResourceRecordSet
|
||||||
|
|
||||||
|
for _, node := range response.Node.Nodes {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
rrset := ResourceRecordSet{name: name, rrdatas: []string{}, rrsets: &rrsets}
|
||||||
|
ip := net.ParseIP(service.Host)
|
||||||
|
switch {
|
||||||
|
case ip == nil:
|
||||||
|
rrset.rrsType = rrstype.CNAME
|
||||||
|
case ip.To4() != nil:
|
||||||
|
rrset.rrsType = rrstype.A
|
||||||
|
case ip.To16() != nil:
|
||||||
|
rrset.rrsType = rrstype.AAAA
|
||||||
|
default:
|
||||||
|
// Cannot occur
|
||||||
|
}
|
||||||
|
rrset.rrdatas = append(rrset.rrdatas, service.Host)
|
||||||
|
rrset.ttl = int64(service.TTL)
|
||||||
|
list = append(list, rrset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rrsets ResourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset {
|
||||||
|
return &ResourceRecordChangeset{
|
||||||
|
zone: rrsets.zone,
|
||||||
|
rrsets: &rrsets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rrsets ResourceRecordSets) New(name string, rrdatas []string, ttl int64, rrsType rrstype.RrsType) dnsprovider.ResourceRecordSet {
|
||||||
|
return ResourceRecordSet{
|
||||||
|
name: name,
|
||||||
|
rrdatas: rrdatas,
|
||||||
|
ttl: ttl,
|
||||||
|
rrsType: rrsType,
|
||||||
|
rrsets: &rrsets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the parent zone
|
||||||
|
func (rrset ResourceRecordSets) Zone() dnsprovider.Zone {
|
||||||
|
return rrset.zone
|
||||||
|
}
|
||||||
|
|
||||||
|
func emptyResponse(resp *etcdc.Response) bool {
|
||||||
|
return resp == nil || resp.Node == nil || (len(resp.Node.Value) == 0 && len(resp.Node.Nodes) == 0)
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
etcd "github.com/coreos/etcd/client"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TestResourceRecordSetsDifferentType 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)
|
||||||
|
}
|
|
@ -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_
|
||||||
|
}
|
|
@ -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.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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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}}
|
||||||
|
}
|
|
@ -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 }
|
||||||
|
)
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
Pages(ctx context.Context, f func(ResourceRecordSetsListResponse) error) 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 {
|
||||||
|
List(project string, managedZone string) ResourceRecordSetsListCall
|
||||||
|
// Get returns a list of resources records with the matching name
|
||||||
|
Get(project, managedZone, name string) ResourceRecordSetsListCall
|
||||||
|
// NewResourceRecordSetsService(s *Service) *ResourceRecordSetsService // TODO: add to service as needed
|
||||||
|
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
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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...)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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/apimachinery/pkg/util/uuid"
|
||||||
|
"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 *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}}
|
||||||
|
}
|
|
@ -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 }
|
|
@ -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 internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
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) Pages(ctx context.Context, f func(interfaces.ResourceRecordSetsListResponse) error) error {
|
||||||
|
return call.impl.Pages(ctx, func(page *dns.ResourceRecordSetsListResponse) error {
|
||||||
|
return f(&ResourceRecordSetsListResponse{page})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
|
@ -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"
|
||||||
|
"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) Get(project, managedZone, name string) interfaces.ResourceRecordSetsListCall {
|
||||||
|
return &ResourceRecordSetsListCall{service.impl.List(project, managedZone).Name(name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
|
@ -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}
|
||||||
|
}
|
|
@ -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_
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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}
|
||||||
|
}
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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_
|
||||||
|
}
|
|
@ -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}
|
||||||
|
}
|
|
@ -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_ }
|
|
@ -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 (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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) Pages(ctx context.Context, f func(interfaces.ResourceRecordSetsListResponse) error) error {
|
||||||
|
return f(call.Response_)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
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) managedZone(project, managedZone string) (*ManagedZone, error) {
|
||||||
|
p := s.Service.ManagedZones_.Impl[project]
|
||||||
|
if p == nil {
|
||||||
|
return nil, fmt.Errorf("Project not found: %s", project)
|
||||||
|
}
|
||||||
|
z := s.Service.ManagedZones_.Impl[project][managedZone]
|
||||||
|
if z == nil {
|
||||||
|
return nil, fmt.Errorf("Zone %s not found in project %s", managedZone, project)
|
||||||
|
}
|
||||||
|
return z.(*ManagedZone), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s ResourceRecordSetsService) List(project string, managedZone string) interfaces.ResourceRecordSetsListCall {
|
||||||
|
if s.ListCall != nil {
|
||||||
|
return s.ListCall
|
||||||
|
}
|
||||||
|
zone, err := s.managedZone(project, managedZone)
|
||||||
|
if err != nil {
|
||||||
|
return &ResourceRecordSetsListCall{Err_: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ResourceRecordSetsListResponse{}
|
||||||
|
for _, set := range zone.Rrsets {
|
||||||
|
response.impl = append(response.impl, set)
|
||||||
|
}
|
||||||
|
return &ResourceRecordSetsListCall{Response_: response}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s ResourceRecordSetsService) Get(project, managedZone, name string) interfaces.ResourceRecordSetsListCall {
|
||||||
|
if s.ListCall != nil {
|
||||||
|
return s.ListCall
|
||||||
|
}
|
||||||
|
zone, err := s.managedZone(project, managedZone)
|
||||||
|
if err != nil {
|
||||||
|
return &ResourceRecordSetsListCall{Err_: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ResourceRecordSetsListResponse{}
|
||||||
|
for _, set := range zone.Rrsets {
|
||||||
|
if set.Name_ == name {
|
||||||
|
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
|
||||||
|
}
|
|
@ -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 adherence
|
||||||
|
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_
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
upserts []dnsprovider.ResourceRecordSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.additions = append(c.additions, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.removals = append(c.removals, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset {
|
||||||
|
c.upserts = append(c.upserts, rrset)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) Apply() error {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.upserts) != 0 {
|
||||||
|
// TODO: We could maybe tweak this to fetch just the records we care about
|
||||||
|
// although not clear when this would be a win. N=1 obviously so though...
|
||||||
|
before, err := c.rrsets.List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error fetching recordset images for upsert operation: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
upsertMap := make(map[string]dnsprovider.ResourceRecordSet)
|
||||||
|
for _, upsert := range c.upserts {
|
||||||
|
key := string(upsert.Type()) + "::" + upsert.Name()
|
||||||
|
upsertMap[key] = upsert
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range before {
|
||||||
|
key := string(b.Type()) + "::" + b.Name()
|
||||||
|
upsert := upsertMap[key]
|
||||||
|
if upsert == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
deletions = append(deletions, b.(ResourceRecordSet).impl)
|
||||||
|
additions = append(additions, upsert.(ResourceRecordSet).impl)
|
||||||
|
|
||||||
|
// Mark as seen
|
||||||
|
delete(upsertMap, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything left in the map must be an addition
|
||||||
|
for _, upsert := range upsertMap {
|
||||||
|
additions = append(additions, upsert.(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ResourceRecordChangeset) IsEmpty() bool {
|
||||||
|
return len(c.additions) == 0 && len(c.removals) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceRecordSets returns the parent ResourceRecordSets
|
||||||
|
func (c *ResourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets {
|
||||||
|
return c.rrsets
|
||||||
|
}
|
|
@ -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())
|
||||||
|
}
|
|
@ -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 clouddns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a list of resource records in the given project and
|
||||||
|
// managed zone.
|
||||||
|
// !!CAUTION!! Your memory might explode if you have a huge number of
|
||||||
|
// records in your managed zone.
|
||||||
|
func (rrsets ResourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) {
|
||||||
|
var list []dnsprovider.ResourceRecordSet
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
call := rrsets.impl.List(rrsets.project(), rrsets.zone.impl.Name())
|
||||||
|
err := call.Pages(ctx, func(page interfaces.ResourceRecordSetsListResponse) error {
|
||||||
|
for _, rrset := range page.Rrsets() {
|
||||||
|
list = append(list, ResourceRecordSet{rrset, &rrsets})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rrsets ResourceRecordSets) Get(name string) ([]dnsprovider.ResourceRecordSet, error) {
|
||||||
|
var list []dnsprovider.ResourceRecordSet
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
call := rrsets.impl.Get(rrsets.project(), rrsets.zone.impl.Name(), name)
|
||||||
|
err := call.Pages(ctx, func(page interfaces.ResourceRecordSetsListResponse) error {
|
||||||
|
for _, rrset := range page.Rrsets() {
|
||||||
|
list = append(list, ResourceRecordSet{rrset, &rrsets})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the parent zone
|
||||||
|
func (rrset ResourceRecordSets) Zone() dnsprovider.Zone {
|
||||||
|
return rrset.zone
|
||||||
|
}
|
|
@ -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 adherence
|
||||||
|
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()
|
||||||
|
}
|
|
@ -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 adherence
|
||||||
|
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()
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
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()
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
defer sets.StartChangeset().Remove(newRrset).Apply()
|
||||||
|
t.Logf("Correctly replaced resource record %v -> %v", rrset, newRrset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CommonTestResourceRecordSetsDifferentType 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()
|
||||||
|
|
||||||
|
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 {
|
||||||
|
rrsetList, err := rrsets.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get recordset: %v", err)
|
||||||
|
} else if len(rrsetList) == 0 {
|
||||||
|
t.Logf("Did not Get recordset: %v", name)
|
||||||
|
} else {
|
||||||
|
t.Logf("Got recordset: %v", rrsetList[0].Name())
|
||||||
|
}
|
||||||
|
return rrsetList
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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" {
|
||||||
|
foundList := getRrOrFail(t, rrsets, rrset.Name())
|
||||||
|
for i, elem := range foundList {
|
||||||
|
if elem.Name() == rrset.Name() && elem.Type() == rrset.Type() {
|
||||||
|
found = foundList[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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 recordset %v: %v", rrset, err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Successfully added resource record set: %v", rrset)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue