mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 17:46:57 +02:00
Fake source (#205)
* Expose inmemory provider to cli So we can test `--source fake` without needing to touch AWS/Google. * Add FakeSource `external-dns --provider inmemory --source fake --dry-run --once` OR `external-dns --provider aws --source fake --fqdn-template <hostname suffix> --dry-run --once` NB: `--fqdn-template` because otherwise we'll default to creating, e.g., `abcd.example.com`, which `--provider aws` filters out because you likely don't have a Zone for `example.com.` Could also be resolved by removing the need to use a real provider; the inmemory provider, perhaps, though it's not entirely hooked up. Closes kubernetes-incubator/external-dns#22 * Style feedback from Travis CI * Improve optionality of kubernetes client * ref(sources): refactor source registration and lookup to be lazy. * Revert "ref: refactor source registration/lookup to be lazily initialized"
This commit is contained in:
parent
85ba7a0ec9
commit
f06fb65917
41
main.go
41
main.go
@ -66,22 +66,39 @@ func main() {
|
||||
go serveMetrics(cfg.MetricsAddress)
|
||||
go handleSigterm(stopChan)
|
||||
|
||||
client, err := newClient(cfg)
|
||||
var client *kubernetes.Clientset
|
||||
|
||||
// create only those services we explicitly ask for in cfg.Sources
|
||||
for _, sourceType := range cfg.Sources {
|
||||
// we only need a k8s client if we're creating a non-fake source, and
|
||||
// have not already instantiated a k8s client
|
||||
if sourceType != "fake" && client == nil {
|
||||
var err error
|
||||
client, err = newClient(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var src source.Source
|
||||
var err error
|
||||
switch sourceType {
|
||||
case "fake":
|
||||
src, err = source.NewFakeSource(cfg.FqdnTemplate)
|
||||
case "service":
|
||||
src, err = source.NewServiceSource(client, cfg.Namespace, cfg.FqdnTemplate, cfg.Compatibility)
|
||||
case "ingress":
|
||||
src, err = source.NewIngressSource(client, cfg.Namespace, cfg.FqdnTemplate)
|
||||
default:
|
||||
log.Fatalf("Don't know how to handle sourceType '%s'", sourceType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
serviceSource, err := source.NewServiceSource(client, cfg.Namespace, cfg.FqdnTemplate, cfg.Compatibility)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
source.Register(sourceType, src)
|
||||
}
|
||||
source.Register("service", serviceSource)
|
||||
|
||||
ingressSource, err := source.NewIngressSource(client, cfg.Namespace, cfg.FqdnTemplate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
source.Register("ingress", ingressSource)
|
||||
|
||||
sources, err := source.LookupMultiple(cfg.Sources)
|
||||
if err != nil {
|
||||
@ -96,6 +113,8 @@ func main() {
|
||||
p, err = provider.NewGoogleProvider(cfg.GoogleProject, cfg.DomainFilter, cfg.DryRun)
|
||||
case "aws":
|
||||
p, err = provider.NewAWSProvider(cfg.DomainFilter, cfg.DryRun)
|
||||
case "inmemory":
|
||||
p, err = provider.NewInMemoryProviderWithDomainAndLogging("example.com"), nil
|
||||
default:
|
||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||
}
|
||||
|
@ -87,13 +87,13 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("kubeconfig", "Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect)").Default(defaultConfig.KubeConfig).StringVar(&cfg.KubeConfig)
|
||||
|
||||
// Flags related to processing sources
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress")
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, fake)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "fake")
|
||||
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
|
||||
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves (optional)").Default(defaultConfig.FqdnTemplate).StringVar(&cfg.FqdnTemplate)
|
||||
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional)").Default(defaultConfig.FqdnTemplate).StringVar(&cfg.FqdnTemplate)
|
||||
app.Flag("compatibility", "Process annotation semantics from legacy implementations (optional, options: mate, molecule)").Default(defaultConfig.Compatibility).EnumVar(&cfg.Compatibility, "", "mate", "molecule")
|
||||
|
||||
// Flags related to providers
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google")
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "inmemory")
|
||||
app.Flag("google-project", "When using the Google provider, specify the Google project (required when --provider=google)").Default(defaultConfig.GoogleProject).StringVar(&cfg.GoogleProject)
|
||||
app.Flag("domain-filter", "Limit possible target zones by a domain suffix (optional)").Default(defaultConfig.DomainFilter).StringVar(&cfg.DomainFilter)
|
||||
|
||||
|
@ -20,6 +20,8 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
"github.com/kubernetes-incubator/external-dns/plan"
|
||||
)
|
||||
@ -58,6 +60,35 @@ func NewInMemoryProvider() *InMemoryProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// NewInMemoryProviderWithDomainAndLogging returns InMemoryProvider DNS provider interface
|
||||
// implementation with a specified domain
|
||||
func NewInMemoryProviderWithDomainAndLogging(domain string) *InMemoryProvider {
|
||||
im := &InMemoryProvider{
|
||||
filter: &filter{},
|
||||
OnApplyChanges: func(changes *plan.Changes) {
|
||||
for _, v := range changes.Create {
|
||||
log.Infof("CREATE: %v", v)
|
||||
}
|
||||
for _, v := range changes.UpdateOld {
|
||||
log.Infof("UPDATE (old): %v", v)
|
||||
}
|
||||
for _, v := range changes.UpdateNew {
|
||||
log.Infof("UPDATE (new): %v", v)
|
||||
}
|
||||
for _, v := range changes.Delete {
|
||||
log.Infof("DELETE: %v", v)
|
||||
}
|
||||
},
|
||||
OnRecords: func() {},
|
||||
domain: domain,
|
||||
client: newInMemoryClient(),
|
||||
}
|
||||
|
||||
im.CreateZone(domain)
|
||||
|
||||
return im
|
||||
}
|
||||
|
||||
// CreateZone adds new zone if not present
|
||||
func (im *InMemoryProvider) CreateZone(newZone string) error {
|
||||
return im.client.CreateZone(newZone)
|
||||
|
102
source/fake.go
Normal file
102
source/fake.go
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2017 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: currently only supports IP targets (A records), not hostname targets
|
||||
*/
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// fakeSource is an implementation of Source for that provides dummy endpoints
|
||||
// for testing/dry-running of dns providers without needing an attached
|
||||
// kubernetes cluster.
|
||||
|
||||
type fakeSource struct {
|
||||
dnsName string
|
||||
}
|
||||
|
||||
const (
|
||||
defaultDNSName = "example.com"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// NewFakeSource creates a new fakeSource with the given client and namespace scope.
|
||||
func NewFakeSource(dnsName string) (Source, error) {
|
||||
if dnsName == "" {
|
||||
dnsName = defaultDNSName
|
||||
}
|
||||
|
||||
return &fakeSource{
|
||||
dnsName: dnsName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Endpoints returns endpoint objects
|
||||
func (sc *fakeSource) Endpoints() ([]*endpoint.Endpoint, error) {
|
||||
endpoints := make([]*endpoint.Endpoint, 10)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
endpoints[i], _ = sc.generateEndpoint()
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
func (sc *fakeSource) generateEndpoint() (*endpoint.Endpoint, error) {
|
||||
endpoint := endpoint.NewEndpoint(
|
||||
generateDNSName(4, sc.dnsName),
|
||||
generateIPAddress(),
|
||||
"A",
|
||||
)
|
||||
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
func generateIPAddress() string {
|
||||
// 192.0.2.[1-255] is reserved by RFC 5737 for documentation and examples
|
||||
return net.IPv4(
|
||||
byte(192),
|
||||
byte(0),
|
||||
byte(2),
|
||||
byte(rand.Intn(253)+1),
|
||||
).String()
|
||||
}
|
||||
|
||||
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func generateDNSName(prefixLength int, dnsName string) string {
|
||||
prefixBytes := make([]rune, prefixLength)
|
||||
|
||||
for i := range prefixBytes {
|
||||
prefixBytes[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||
}
|
||||
|
||||
prefixStr := string(prefixBytes)
|
||||
|
||||
return fmt.Sprintf("%s.%s", prefixStr, dnsName)
|
||||
}
|
72
source/fake_test.go
Normal file
72
source/fake_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2017 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 source
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
|
||||
func generateTestEndpoints() []*endpoint.Endpoint {
|
||||
sc, _ := NewFakeSource("")
|
||||
|
||||
endpoints, _ := sc.Endpoints()
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func TestFakeSourceReturnsTenEndpoints(t *testing.T) {
|
||||
endpoints := generateTestEndpoints()
|
||||
|
||||
count := len(endpoints)
|
||||
|
||||
if count != 10 {
|
||||
t.Error(count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeEndpointsBelongToDomain(t *testing.T) {
|
||||
validRecord := regexp.MustCompile(`^[a-z]{4}\.example\.com$`)
|
||||
|
||||
endpoints := generateTestEndpoints()
|
||||
|
||||
for _, e := range endpoints {
|
||||
valid := validRecord.MatchString(e.DNSName)
|
||||
|
||||
if !valid {
|
||||
t.Error(e.DNSName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeEndpointsResolveToIPAddresses(t *testing.T) {
|
||||
endpoints := generateTestEndpoints()
|
||||
|
||||
for _, e := range endpoints {
|
||||
ip := net.ParseIP(e.Target)
|
||||
|
||||
if ip == nil {
|
||||
t.Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that FakeSource is a source
|
||||
var _ Source = &fakeSource{}
|
Loading…
Reference in New Issue
Block a user