diff --git a/glide.yaml b/glide.yaml index d0e0bfcd2..6254ee6eb 100644 --- a/glide.yaml +++ b/glide.yaml @@ -58,4 +58,6 @@ import: - package: github.com/digitalocean/godo version: ~1.1.0 - package: github.com/coreos/go-oidc +- package: github.com/coreos/etcd + version: ^3.2.1 - package: github.com/infobloxopen/infoblox-go-client diff --git a/main.go b/main.go index cf32f958e..5177521fa 100644 --- a/main.go +++ b/main.go @@ -118,6 +118,8 @@ func main() { ) case "inmemory": p, err = provider.NewInMemoryProvider(provider.InMemoryInitZones(cfg.InMemoryZones), provider.InMemoryWithDomain(domainFilter), provider.InMemoryWithLogging()), nil + case "coredns", "skydns": + p, err = provider.NewCoreDNSProvider(domainFilter, cfg.ETCD, cfg.DryRun) default: log.Fatalf("unknown dns provider: %s", cfg.Provider) } diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go index 857a7afa9..eec350c0c 100644 --- a/pkg/apis/externaldns/types.go +++ b/pkg/apis/externaldns/types.go @@ -61,6 +61,8 @@ type Config struct { LogFormat string MetricsAddress string LogLevel string + Debug bool + ETCD string } var defaultConfig = &Config{ @@ -95,6 +97,7 @@ var defaultConfig = &Config{ LogFormat: "text", MetricsAddress: ":7979", LogLevel: logrus.InfoLevel.String(), + ETCD: "http://localhost:2379", } // NewConfig returns new Config object @@ -129,13 +132,14 @@ func (cfg *Config) ParseFlags(args []string) error { app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal) // Flags related to providers - app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory") + app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, google, azure, cloudflare, digitalocean, dnsimple, infoblox, inmemory, coredns, skydns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "google", "azure", "cloudflare", "digitalocean", "dnsimple", "infoblox", "inmemory", "coredns", "skydns") app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter) 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("aws-zone-type", "When using the AWS provider, filter for zones of this type (optional, options: public, private)").Default(defaultConfig.AWSZoneType).EnumVar(&cfg.AWSZoneType, "", "public", "private") app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile) app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup) app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied) + app.Flag("etcd", "ETCD cluster URI(s) for coredns/skydns provider").Default(defaultConfig.ETCD).StringVar(&cfg.ETCD) app.Flag("infoblox-grid-host", "When using the Infoblox provider, specify the Grid Manager host (required when --provider=infoblox)").Default(defaultConfig.InfobloxGridHost).StringVar(&cfg.InfobloxGridHost) app.Flag("infoblox-wapi-port", "When using the Infoblox provider, specify the WAPI port (default: 443)").Default(strconv.Itoa(defaultConfig.InfobloxWapiPort)).IntVar(&cfg.InfobloxWapiPort) app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername) diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go index d723427fd..80349444e 100644 --- a/pkg/apis/externaldns/types_test.go +++ b/pkg/apis/externaldns/types_test.go @@ -58,6 +58,7 @@ var ( LogFormat: "text", MetricsAddress: ":7979", LogLevel: logrus.InfoLevel.String(), + ETCD: "http://localhost:2379", } overriddenConfig = &Config{ @@ -91,6 +92,7 @@ var ( LogFormat: "json", MetricsAddress: "127.0.0.1:9099", LogLevel: logrus.DebugLevel.String(), + ETCD: "http://host:3378,http://host:3379", } ) @@ -146,6 +148,7 @@ func TestParseFlags(t *testing.T) { "--log-format=json", "--metrics-address=127.0.0.1:9099", "--log-level=debug", + "--etcd=http://host:3378,http://host:3379", }, envVars: map[string]string{}, expected: overriddenConfig, @@ -184,6 +187,7 @@ func TestParseFlags(t *testing.T) { "EXTERNAL_DNS_LOG_FORMAT": "json", "EXTERNAL_DNS_METRICS_ADDRESS": "127.0.0.1:9099", "EXTERNAL_DNS_LOG_LEVEL": "debug", + "EXTERNAL_DNS_ETCD": "http://host:3378,http://host:3379", }, expected: overriddenConfig, }, diff --git a/provider/coredns.go b/provider/coredns.go new file mode 100644 index 000000000..9835da9f9 --- /dev/null +++ b/provider/coredns.go @@ -0,0 +1,268 @@ +/* +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 provider + +import ( + "container/list" + "encoding/json" + "net" + "strings" + + log "github.com/Sirupsen/logrus" + etcd "github.com/coreos/etcd/client" + "golang.org/x/net/context" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" +) + +// skyDNSClient is an interface to work with SkyDNS service records in etcd +type skyDNSClient interface { + GetServices(prefix string) ([]*Service, error) + SaveService(value *Service) error + DeleteService(key string) error +} + +type coreDNSProvider struct { + dryRun bool + domainFilter DomainFilter + client skyDNSClient +} + +// Service represents SkyDNS/CoreDNS etcd record +type Service struct { + Host string `json:"host,omitempty"` + Port int `json:"port,omitempty"` + Priority int `json:"priority,omitempty"` + Weight int `json:"weight,omitempty"` + Text string `json:"text,omitempty"` + Mail bool `json:"mail,omitempty"` // Be an MX record. Priority becomes Preference. + TTL uint32 `json:"ttl,omitempty"` + + // When a SRV record with a "Host: IP-address" is added, we synthesize + // a srv.Target domain name. Normally we convert the full Key where + // the record lives to a DNS name and use this as the srv.Target. When + // TargetStrip > 0 we strip the left most TargetStrip labels from the + // DNS name. + TargetStrip int `json:"targetstrip,omitempty"` + + // Group is used to group (or *not* to group) different services + // together. Services with an identical Group are returned in the same + // answer. + Group string `json:"group,omitempty"` + + // Etcd key where we found this service and ignored from json un-/marshalling + Key string `json:"-"` +} + +type etcdClient struct { + api etcd.KeysAPI +} + +var _ skyDNSClient = etcdClient{} + +// GetService return all Service records stored in etcd stored anywhere under the given key (recursively) +func (c etcdClient) GetServices(prefix string) ([]*Service, error) { + var result []*Service + opts := &etcd.GetOptions{Recursive: true} + data, err := c.api.Get(context.Background(), prefix, opts) + if err != nil { + if etcd.IsKeyNotFound(err) { + return nil, nil + } + return nil, err + } + + queue := list.New() + queue.PushFront(data.Node) + for queueNode := queue.Front(); queueNode != nil; queueNode = queueNode.Next() { + node := queueNode.Value.(*etcd.Node) + if node.Dir { + for _, childNode := range node.Nodes { + queue.PushBack(childNode) + } + continue + } + service := &Service{} + err = json.Unmarshal([]byte(node.Value), service) + if err != nil { + log.Error("Cannot parse JSON value ", node.Value) + continue + } + service.Key = node.Key + result = append(result, service) + } + return result, nil +} + +// SaveService persists service data into etcd +func (c etcdClient) SaveService(service *Service) error { + value, err := json.Marshal(&service) + if err != nil { + return err + } + _, err = c.api.Set(context.Background(), service.Key, string(value), nil) + if err != nil { + return err + } + return nil +} + +// DeleteService deletes service record from etcd +func (c etcdClient) DeleteService(key string) error { + _, err := c.api.Delete(context.Background(), key, nil) + return err + +} + +//newETCDClient is an etcd client constructor +func newETCDClient(etcdURIs string) (skyDNSClient, error) { + cfg := etcd.Config{Endpoints: strings.Split(etcdURIs, ",")} + c, err := etcd.New(cfg) + if err != nil { + return nil, err + } + return etcdClient{etcd.NewKeysAPI(c)}, nil +} + +// NewCoreDNSProvider is a CoreDNS provider constructor +func NewCoreDNSProvider(domainFilter DomainFilter, etcdURIs string, dryRun bool) (Provider, error) { + client, err := newETCDClient(etcdURIs) + if err != nil { + return nil, err + } + return coreDNSProvider{ + client: client, + dryRun: dryRun, + domainFilter: domainFilter, + }, nil +} + +// Records returns all DNS records found in SkyDNS/CoreDNS etcd backend. Depending on the record fields +// it may be mapped to one or two records of type A, CNAME, TXT, A+TXT, CNAME+TXT +func (p coreDNSProvider) Records() ([]*endpoint.Endpoint, error) { + var result []*endpoint.Endpoint + services, err := p.client.GetServices("/skydns") + if err != nil { + return nil, err + } + for _, service := range services { + domains := strings.Split(strings.TrimPrefix(service.Key, "/skydns/"), "/") + reverse(domains) + dnsName := strings.Join(domains, ".") + if !p.domainFilter.Match(dnsName) { + continue + } + if service.Host != "" { + ep := endpoint.NewEndpoint( + dnsName, + service.Host, + guessRecordType(service.Host), + ) + ep.Labels["originalText"] = service.Text + result = append(result, ep) + } + if service.Text != "" { + ep := endpoint.NewEndpoint( + dnsName, + service.Text, + endpoint.RecordTypeTXT, + ) + result = append(result, ep) + } + } + return result, nil +} + +// ApplyChanges stores changes back to etcd converting them to SkyDNS format and aggregating A/CNAME and TXT records +func (p coreDNSProvider) ApplyChanges(changes *plan.Changes) error { + grouped := map[string][]*endpoint.Endpoint{} + for _, ep := range changes.Create { + grouped[ep.DNSName] = append(grouped[ep.DNSName], ep) + } + for _, ep := range changes.UpdateNew { + grouped[ep.DNSName] = append(grouped[ep.DNSName], ep) + } + for dnsName, group := range grouped { + if !p.domainFilter.Match(dnsName) { + log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", dnsName) + continue + } + service := Service{} + for _, ep := range group { + switch ep.RecordType { + case endpoint.RecordTypeA: + service.Host = ep.Target + case endpoint.RecordTypeCNAME: + if service.Host == "" { + service.Host = ep.Target + } + case endpoint.RecordTypeTXT: + service.Text = ep.Target + default: + log.Error("Unsupported record type", ep.RecordType) + continue + } + if service.Text == "" { + service.Text = ep.Labels["originalText"] + } + } + if service.Host != "" || service.Text != "" { + service.Key = etcdKeyFor(dnsName) + log.Infof("Add/set key %s to Host=%s, Text=%s", service.Key, service.Host, service.Text) + if !p.dryRun { + err := p.client.SaveService(&service) + if err != nil { + return err + } + } + } + } + + for _, ep := range changes.Delete { + key := etcdKeyFor(ep.DNSName) + log.Infof("Delete key %s", key) + if !p.dryRun { + err := p.client.DeleteService(key) + if err != nil { + return err + } + } + } + + return nil +} + +func guessRecordType(target string) string { + if net.ParseIP(target) != nil { + return endpoint.RecordTypeA + } + return endpoint.RecordTypeCNAME +} + +func etcdKeyFor(dnsName string) string { + domains := strings.Split(dnsName, ".") + reverse(domains) + return "/skydns/" + strings.Join(domains, "/") +} + +func reverse(slice []string) { + for i := 0; i < len(slice)/2; i++ { + j := len(slice) - i - 1 + slice[i], slice[j] = slice[j], slice[i] + } +} diff --git a/provider/coredns_test.go b/provider/coredns_test.go new file mode 100644 index 000000000..108f65504 --- /dev/null +++ b/provider/coredns_test.go @@ -0,0 +1,289 @@ +/* +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 provider + +import ( + "strings" + "testing" + + "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/plan" +) + +type fakeETCDClient struct { + services map[string]*Service +} + +func (c fakeETCDClient) GetServices(prefix string) ([]*Service, error) { + var result []*Service + for key, value := range c.services { + if strings.HasPrefix(key, prefix) { + value.Key = key + result = append(result, value) + } + } + return result, nil +} + +func (c fakeETCDClient) SaveService(service *Service) error { + c.services[service.Key] = service + return nil +} + +func (c fakeETCDClient) DeleteService(key string) error { + delete(c.services, key) + return nil +} + +func TestAServiceTranslation(t *testing.T) { + expectedTarget := "1.2.3.4" + expectedDNSName := "example.com" + expectedRecordType := endpoint.RecordTypeA + + client := fakeETCDClient{ + map[string]*Service{ + "/skydns/com/example": {Host: expectedTarget}, + }, + } + provider := coreDNSProvider{client: client} + endpoints, err := provider.Records() + if err != nil { + t.Fatal(err) + } + if len(endpoints) != 1 { + t.Fatalf("got unexpected number of endpoints: %d", len(endpoints)) + } + if endpoints[0].DNSName != expectedDNSName { + t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName) + } + if endpoints[0].Target != expectedTarget { + t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Target, expectedTarget) + } + if endpoints[0].RecordType != expectedRecordType { + t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType) + } +} + +func TestCNAMEServiceTranslation(t *testing.T) { + expectedTarget := "example.net" + expectedDNSName := "example.com" + expectedRecordType := endpoint.RecordTypeCNAME + + client := fakeETCDClient{ + map[string]*Service{ + "/skydns/com/example": {Host: expectedTarget}, + }, + } + provider := coreDNSProvider{client: client} + endpoints, err := provider.Records() + if err != nil { + t.Fatal(err) + } + if len(endpoints) != 1 { + t.Fatalf("got unexpected number of endpoints: %d", len(endpoints)) + } + if endpoints[0].DNSName != expectedDNSName { + t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName) + } + if endpoints[0].Target != expectedTarget { + t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Target, expectedTarget) + } + if endpoints[0].RecordType != expectedRecordType { + t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType) + } +} + +func TestTXTServiceTranslation(t *testing.T) { + expectedTarget := "string" + expectedDNSName := "example.com" + expectedRecordType := endpoint.RecordTypeTXT + + client := fakeETCDClient{ + map[string]*Service{ + "/skydns/com/example": {Text: expectedTarget}, + }, + } + provider := coreDNSProvider{client: client} + endpoints, err := provider.Records() + if err != nil { + t.Fatal(err) + } + if len(endpoints) != 1 { + t.Fatalf("got unexpected number of endpoints: %d", len(endpoints)) + } + if endpoints[0].DNSName != expectedDNSName { + t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName) + } + if endpoints[0].Target != expectedTarget { + t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Target, expectedTarget) + } + if endpoints[0].RecordType != expectedRecordType { + t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType) + } +} + +func TestAWithTXTServiceTranslation(t *testing.T) { + expectedTargets := map[string]string{ + endpoint.RecordTypeA: "1.2.3.4", + endpoint.RecordTypeTXT: "string", + } + expectedDNSName := "example.com" + + client := fakeETCDClient{ + map[string]*Service{ + "/skydns/com/example": {Host: "1.2.3.4", Text: "string"}, + }, + } + provider := coreDNSProvider{client: client} + endpoints, err := provider.Records() + if err != nil { + t.Fatal(err) + } + if len(endpoints) != len(expectedTargets) { + t.Fatalf("got unexpected number of endpoints: %d", len(endpoints)) + } + + for _, ep := range endpoints { + expectedTarget := expectedTargets[ep.RecordType] + if expectedTarget == "" { + t.Errorf("got unexpected DNS record type: %s", ep.RecordType) + continue + } + delete(expectedTargets, ep.RecordType) + + if ep.DNSName != expectedDNSName { + t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName) + } + + if ep.Target != expectedTarget { + t.Errorf("got unexpected DNS target: %s != %s", ep.Target, expectedTarget) + } + } +} + +func TestCNAMEWithTXTServiceTranslation(t *testing.T) { + expectedTargets := map[string]string{ + endpoint.RecordTypeCNAME: "example.net", + endpoint.RecordTypeTXT: "string", + } + expectedDNSName := "example.com" + + client := fakeETCDClient{ + map[string]*Service{ + "/skydns/com/example": {Host: "example.net", Text: "string"}, + }, + } + provider := coreDNSProvider{client: client} + endpoints, err := provider.Records() + if err != nil { + t.Fatal(err) + } + if len(endpoints) != len(expectedTargets) { + t.Fatalf("got unexpected number of endpoints: %d", len(endpoints)) + } + + for _, ep := range endpoints { + expectedTarget := expectedTargets[ep.RecordType] + if expectedTarget == "" { + t.Errorf("got unexpected DNS record type: %s", ep.RecordType) + continue + } + delete(expectedTargets, ep.RecordType) + + if ep.DNSName != expectedDNSName { + t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName) + } + + if ep.Target != expectedTarget { + t.Errorf("got unexpected DNS target: %s != %s", ep.Target, expectedTarget) + } + } +} + +func TestCoreDNSApplyChanges(t *testing.T) { + client := fakeETCDClient{ + map[string]*Service{}, + } + coredns := coreDNSProvider{client: client} + + changes1 := &plan.Changes{ + Create: []*endpoint.Endpoint{ + endpoint.NewEndpoint("domain1.local", "5.5.5.5", endpoint.RecordTypeA), + endpoint.NewEndpoint("domain1.local", "string1", endpoint.RecordTypeTXT), + endpoint.NewEndpoint("domain2.local", "site.local", endpoint.RecordTypeCNAME), + }, + } + coredns.ApplyChanges(changes1) + + expectedServices1 := map[string]*Service{ + "/skydns/local/domain1": {Host: "5.5.5.5", Text: "string1"}, + "/skydns/local/domain2": {Host: "site.local"}, + } + validateServices(client.services, expectedServices1, t, 1) + + updatedEp := endpoint.NewEndpoint("domain1.local", "6.6.6.6", endpoint.RecordTypeA) + updatedEp.Labels["originalText"] = "string1" + changes2 := &plan.Changes{ + Create: []*endpoint.Endpoint{ + endpoint.NewEndpoint("domain3.local", "7.7.7.7", endpoint.RecordTypeA), + }, + UpdateNew: []*endpoint.Endpoint{updatedEp}, + } + coredns.ApplyChanges(changes2) + + expectedServices2 := map[string]*Service{ + "/skydns/local/domain1": {Host: "6.6.6.6", Text: "string1"}, + "/skydns/local/domain2": {Host: "site.local"}, + "/skydns/local/domain3": {Host: "7.7.7.7"}, + } + validateServices(client.services, expectedServices2, t, 2) + + changes3 := &plan.Changes{ + Delete: []*endpoint.Endpoint{ + endpoint.NewEndpoint("domain1.local", "6.6.6.6", endpoint.RecordTypeA), + endpoint.NewEndpoint("domain1.local", "string", endpoint.RecordTypeTXT), + endpoint.NewEndpoint("domain3.local", "7.7.7.7", endpoint.RecordTypeA), + }, + } + + coredns.ApplyChanges(changes3) + + expectedServices3 := map[string]*Service{ + "/skydns/local/domain2": {Host: "site.local"}, + } + validateServices(client.services, expectedServices3, t, 3) +} + +func validateServices(services, expectedServices map[string]*Service, t *testing.T, step int) { + if len(services) != len(expectedServices) { + t.Errorf("wrong number of records on step %d: %d != %d", step, len(services), len(expectedServices)) + } + for key, value := range services { + expectedService := expectedServices[key] + if expectedService == nil { + t.Errorf("unexpected service %s", key) + continue + } + delete(expectedServices, key) + if value.Host != expectedService.Host { + t.Errorf("wrong host for service %s: %s != %s on step %d", key, value.Host, expectedService.Host, step) + } + if value.Text != expectedService.Text { + t.Errorf("wrong text for service %s: %s != %s on step %d", key, value.Text, expectedService.Text, step) + } + } +} diff --git a/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt b/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt deleted file mode 100644 index b9a2ff984..000000000 --- a/vendor/bitbucket.org/ww/goautoneg/.hg_archival.txt +++ /dev/null @@ -1,6 +0,0 @@ -repo: 848b351341922ce39becda978778724d5b58dbca -node: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 -branch: default -latesttag: null -latesttagdistance: 5 -changessincelatesttag: 5 diff --git a/vendor/bitbucket.org/ww/goautoneg/Makefile b/vendor/bitbucket.org/ww/goautoneg/Makefile deleted file mode 100644 index e33ee1730..000000000 --- a/vendor/bitbucket.org/ww/goautoneg/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=bitbucket.org/ww/goautoneg -GOFILES=autoneg.go - -include $(GOROOT)/src/Make.pkg - -format: - gofmt -w *.go - -docs: - gomake clean - godoc ${TARG} > README.txt diff --git a/vendor/github.com/coreos/etcd/.dockerignore b/vendor/github.com/coreos/etcd/.dockerignore new file mode 100644 index 000000000..6b8710a71 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md b/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..c8c894c26 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ +# Bug reporting + +A good bug report has some very specific qualities, so please read over our short document on [reporting bugs][report_bugs] before submitting a bug report. + +To ask a question, go ahead and ignore this. + +[report_bugs]: https://github.com/coreos/etcd/blob/master/Documentation/reporting_bugs.md diff --git a/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..4fa34e9b3 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +# Contributing guidelines + +Please read our [contribution workflow][contributing] before submitting a pull request. + +[contributing]: https://github.com/coreos/etcd/blob/master/CONTRIBUTING.md#contribution-flow diff --git a/vendor/github.com/coreos/etcd/.gitignore b/vendor/github.com/coreos/etcd/.gitignore new file mode 100644 index 000000000..210be6fe9 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.gitignore @@ -0,0 +1,15 @@ +/coverage +/gopath +/gopath.proto +/go-bindata +/machine* +/bin +.vagrant +*.etcd +/etcd +*.swp +/hack/insta-discovery/.env +*.test +tools/functional-tester/docker/bin +hack/tls-setup/certs +.idea diff --git a/vendor/github.com/coreos/etcd/.godir b/vendor/github.com/coreos/etcd/.godir new file mode 100644 index 000000000..00ff6aa80 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.godir @@ -0,0 +1 @@ +github.com/coreos/etcd diff --git a/vendor/github.com/coreos/etcd/.header b/vendor/github.com/coreos/etcd/.header new file mode 100644 index 000000000..0446af6d8 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.header @@ -0,0 +1,13 @@ +// Copyright 2016 The etcd 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. diff --git a/vendor/github.com/coreos/etcd/.travis.yml b/vendor/github.com/coreos/etcd/.travis.yml new file mode 100644 index 000000000..fb8e9b832 --- /dev/null +++ b/vendor/github.com/coreos/etcd/.travis.yml @@ -0,0 +1,83 @@ +dist: trusty +language: go +go_import_path: github.com/coreos/etcd +sudo: false + +go: + - 1.8.3 + - tip + +notifications: + on_success: never + on_failure: never + +env: + matrix: + - TARGET=amd64 + - TARGET=darwin-amd64 + - TARGET=windows-amd64 + - TARGET=arm64 + - TARGET=arm + - TARGET=386 + - TARGET=ppc64le + +matrix: + fast_finish: true + allow_failures: + - go: tip + exclude: + - go: tip + env: TARGET=darwin-amd64 + - go: tip + env: TARGET=windows-amd64 + - go: tip + env: TARGET=arm + - go: tip + env: TARGET=arm64 + - go: tip + env: TARGET=386 + - go: tip + env: TARGET=ppc64le + +addons: + apt: + sources: + - debian-sid + packages: + - libpcap-dev + - libaspell-dev + - libhunspell-dev + - shellcheck + +before_install: + - go get -v -u github.com/chzchzchz/goword + - go get -v -u github.com/coreos/license-bill-of-materials + - go get -v -u honnef.co/go/tools/cmd/gosimple + - go get -v -u honnef.co/go/tools/cmd/unused + - go get -v -u honnef.co/go/tools/cmd/staticcheck + - ./scripts/install-marker.sh amd64 + +# disable godep restore override +install: + - pushd cmd/etcd && go get -t -v ./... && popd + +script: + - > + case "${TARGET}" in + amd64) + GOARCH=amd64 ./test + ;; + darwin-amd64) + GO_BUILD_FLAGS="-a -v" GOPATH="" GOOS=darwin GOARCH=amd64 ./build + ;; + windows-amd64) + GO_BUILD_FLAGS="-a -v" GOPATH="" GOOS=windows GOARCH=amd64 ./build + ;; + 386) + GOARCH=386 PASSES="build unit" ./test + ;; + *) + # test building out of gopath + GO_BUILD_FLAGS="-a -v" GOPATH="" GOARCH="${TARGET}" ./build + ;; + esac diff --git a/vendor/github.com/coreos/etcd/CONTRIBUTING.md b/vendor/github.com/coreos/etcd/CONTRIBUTING.md new file mode 100644 index 000000000..635f73a30 --- /dev/null +++ b/vendor/github.com/coreos/etcd/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# How to contribute + +etcd is Apache 2.0 licensed and accepts contributions via GitHub pull requests. This document outlines some of the conventions on commit message formatting, contact points for developers, and other resources to help get contributions into etcd. + +# Email and chat + +- Email: [etcd-dev](https://groups.google.com/forum/?hl=en#!forum/etcd-dev) +- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org + +## Getting started + +- Fork the repository on GitHub +- Read the README.md for build instructions + +## Reporting bugs and creating issues + +Reporting bugs is one of the best ways to contribute. However, a good bug report has some very specific qualities, so please read over our short document on [reporting bugs](https://github.com/coreos/etcd/blob/master/Documentation/reporting_bugs.md) before submitting a bug report. This document might contain links to known issues, another good reason to take a look there before reporting a bug. + +## Contribution flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where to base the contribution. This is usually master. +- Make commits of logical units. +- Make sure commit messages are in the proper format (see below). +- Push changes in a topic branch to a personal fork of the repository. +- Submit a pull request to coreos/etcd. +- The PR must receive a LGTM from two maintainers found in the MAINTAINERS file. + +Thanks for contributing! + +### Code style + +The coding style suggested by the Golang community is used in etcd. See the [style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details. + +Please follow this style to make etcd easy to review, maintain and develop. + +### Format of the commit message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that can easily be killed and started for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +