func (c *ResourceRecordChangeset) Apply() error { ctx := context.Background() etcdPathPrefix := c.zone.zones.intf.etcdPathPrefix getOpts := &etcdc.GetOptions{} setOpts := &etcdc.SetOptions{} deleteOpts := &etcdc.DeleteOptions{ Recursive: true, } for _, changeset := range c.changeset { switch changeset.cstype { case ADDITION: for _, rrdata := range changeset.rrset.Rrdatas() { b, err := json.Marshal(&dnsmsg.Service{Host: rrdata, TTL: uint32(changeset.rrset.Ttl()), Group: changeset.rrset.Name()}) if err != nil { return err } recordValue := string(b) recordLabel := getHash(rrdata) recordKey := buildDNSNameString(changeset.rrset.Name(), recordLabel) response, err := c.zone.zones.intf.etcdKeysAPI.Get(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), getOpts) if err == nil && response != nil { return fmt.Errorf("Key already exist, key: %v", recordKey) } _, err = c.zone.zones.intf.etcdKeysAPI.Set(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), recordValue, setOpts) if err != nil { return err } } case DELETION: for _, rrdata := range changeset.rrset.Rrdatas() { recordLabel := getHash(rrdata) recordKey := buildDNSNameString(changeset.rrset.Name(), recordLabel) _, err := c.zone.zones.intf.etcdKeysAPI.Delete(ctx, dnsmsg.Path(recordKey, etcdPathPrefix), deleteOpts) if err != nil { return err } } // TODO: We need to cleanup empty dirs in etcd } } return nil }
// Records looks up records in etcd. If exact is true, it will lookup just // this name. This is used when find matches when completing SRV lookups // for instance. func (g Etcd) Records(name string, exact bool) ([]msg.Service, error) { path, star := msg.PathWithWildcard(name, g.PathPrefix) r, err := g.Get(path, true) if err != nil { return nil, err } segments := strings.Split(msg.Path(name, g.PathPrefix), "/") switch { case exact && r.Node.Dir: return nil, nil case r.Node.Dir: return g.loopNodes(r.Node.Nodes, segments, star, nil) default: return g.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil) } }
func (rrsets ResourceRecordSets) Get(name string) (dnsprovider.ResourceRecordSet, error) { getOpts := &etcdc.GetOptions{ Recursive: true, } etcdPathPrefix := rrsets.zone.zones.intf.etcdPathPrefix response, err := rrsets.zone.zones.intf.etcdKeysAPI.Get(context.Background(), dnsmsg.Path(name, etcdPathPrefix), getOpts) if err != nil { if etcdc.IsKeyNotFound(err) { glog.V(2).Infof("Subdomain %q does not exist", name) return nil, nil } return nil, fmt.Errorf("Failed to get service from etcd, err: %v", err) } if emptyResponse(response) { glog.V(2).Infof("Subdomain %q does not exist in etcd", name) return nil, nil } rrset := ResourceRecordSet{name: name, rrdatas: []string{}, rrsets: &rrsets} found := false for _, node := range response.Node.Nodes { found = true service := dnsmsg.Service{} err = json.Unmarshal([]byte(node.Value), &service) if err != nil { return nil, fmt.Errorf("Failed to unmarshall json data, err: %v", err) } // assuming all rrdatas in a rrset will have same type ip := net.ParseIP(service.Host) switch { case ip == nil: rrset.rrsType = rrstype.CNAME case ip.To4() != nil: rrset.rrsType = rrstype.A } rrset.rrdatas = append(rrset.rrdatas, service.Host) rrset.ttl = int64(service.TTL) } if !found { return nil, nil } return rrset, nil }