func writeEnvironmentFile(kvs map[string]string, w io.Writer) error { var buffer bytes.Buffer // indicate it's going to write envvars log.Printf("writing environment variables...") for k, v := range kvs { buffer.WriteString(fmt.Sprintf("%s=%s\n", k, v)) } if _, err := buffer.WriteTo(w); err != nil { return err } // write done log.Printf("done\n") return nil }
func (eb *Etcd2Bootstrapper) Run() { // sanity checks if eb.config.Me == "" { log.Fatal(fmt.Errorf("-me is a required parameters")) } meMember, err := parseMember(eb.config.Me) if err != nil { log.Fatal(err) } if eb.config.Members == "" { log.Fatal(fmt.Errorf("-members is a required parameters")) } clusterMembers, err := parseMembers(eb.config.Members) if err != nil { log.Fatal(err) } var tmplListen *template.Template = nil if eb.config.TmplListenUrl != "" { tmplListen, _ = template.New("tmpl-listen-url").Parse(eb.config.TmplListenUrl) } var tmplPeer *template.Template = nil if eb.config.TmplPeerUrl != "" { tmplPeer, _ = template.New("tmpl-listen-url").Parse(eb.config.TmplPeerUrl) } if _, err := os.Stat(eb.config.Out); err == nil { log.Printf("etcd-peers file %s already created, exiting.\n", eb.config.Out) return } // create client instantiator var newClientFn func(string) (*etcd.Client, error) = nil if eb.config.CertFile != "" && eb.config.KeyFile != "" && eb.config.CAFile != "" { newClientFn = func(url string) (*etcd.Client, error) { return etcd.NewTLSClient(url, eb.config.CertFile, eb.config.KeyFile, eb.config.CAFile) } } else { newClientFn = func(url string) (*etcd.Client, error) { return etcd.NewClient(url) } } kvs, err := syncEtcdCluster(*meMember, clusterMembers, eb.config.Force, tmplListen, tmplPeer, newClientFn) if err != nil { log.Fatal(err) } tempFilePath := eb.config.Out + ".tmp" tempFile, err := os.Create(tempFilePath) if err != nil { log.Fatal(err) } isClosed := false defer func() { if !isClosed { tempFile.Close() } }() if err := writeEnvironmentFile(kvs, tempFile); err != nil { log.Fatal(err) } if err := tempFile.Close(); err != nil { log.Fatal(err) } isClosed = true if err := os.Rename(tempFilePath, eb.config.Out); err != nil { log.Fatal(err) } return }
func syncEtcdCluster(me member, members []member, force bool, tmplListenUrl, tmplPeerUrl *template.Template, newClientFn func(string) (*etcd.Client, error)) (map[string]string, error) { // retrieve current cluster members var etcdMembers []client.Member var goodEtcdClientURL string = "" for _, member := range members { // what was first, chicken or egg? if member.Address == me.Address { continue } etcdClientURL, err := getListenURL(member.Address, tmplListenUrl) if err != nil { return nil, err } etcdClient, err := newClientFn(etcdClientURL) if err != nil { log.Printf("unable to create etcd client for %s: %v\n", etcdClientURL, err) continue } etcdMembers, err = etcdClient.ListMembers() if err != nil { log.Printf("unable to list etcd members: %v\n", err) continue } else { goodEtcdClientURL = etcdClientURL break } } // etcd parameters var initialClusterState string var initialCluster string // check if instanceId is already member of cluster var isMember bool = false for _, member := range etcdMembers { if member.Name == me.Name { isMember = true break } } // cosmetic parameter which defines if it should join to an existing or create a new cluster joinExistingCluster := etcdMembers != nil && (!isMember || force) // if i am not already listed as a member of the cluster assume that this is a new cluster if joinExistingCluster { log.Printf("joining to an existing cluster, using this client url: %s\n", goodEtcdClientURL) // // detect and remove bad peers // // detect if it's required to add or not isAddRequired := true // create a reverse cluster members map clusterMembersByIp := make(map[string]string) for _, member := range members { clusterMembersByIp[member.Address] = member.Name } for _, etcdMember := range etcdMembers { peerURL, err := url.Parse(etcdMember.PeerURLs[0]) if err != nil { return nil, err } peerHost, _, err := net.SplitHostPort(peerURL.Host) if err != nil { return nil, err } if peerHost == me.Address { isAddRequired = false } _, ok := clusterMembersByIp[peerHost] isRemoveRequired := !ok || (force && etcdMember.Name == me.Name) if isRemoveRequired { log.Printf("removing etcd member: %s...", etcdMember.ID) etcdClient, err := newClientFn(goodEtcdClientURL) if err != nil { return nil, err } err = etcdClient.RemoveMember(etcdMember.ID) if err != nil { return nil, err } log.Printf("done\n") } } // // list current etcd members (after removing spurious ones) // etcdClient, err := newClientFn(goodEtcdClientURL) if err != nil { return nil, err } etcdMembers, err = etcdClient.ListMembers() if err != nil { return nil, err } kvs := make([]string, 0) for _, etcdMember := range etcdMembers { // ignore unstarted peers if len(etcdMember.Name) == 0 { continue } kvs = append(kvs, fmt.Sprintf("%s=%s", etcdMember.Name, etcdMember.PeerURLs[0])) } instancePeerURL, err := getPeerURL(me.Address, tmplPeerUrl) if err != nil { return nil, err } kvs = append(kvs, fmt.Sprintf("%s=%s", me.Name, instancePeerURL)) initialClusterState = "existing" initialCluster = strings.Join(kvs, ",") // // join an existing cluster // if isAddRequired { log.Printf("adding etcd member: %s...", instancePeerURL) etcdClient, err := newClientFn(goodEtcdClientURL) if err != nil { return nil, err } member, err := etcdClient.AddMember(instancePeerURL) if member == nil { return nil, err } log.Printf("done\n") } } else { log.Printf("creating new cluster\n") // initial cluster kvs := make([]string, 0) for _, member := range members { peerURL, err := getPeerURL(member.Address, tmplPeerUrl) if err != nil { return nil, err } kvs = append(kvs, fmt.Sprintf("%s=%s", member.Name, peerURL)) } initialClusterState = "new" initialCluster = strings.Join(kvs, ",") } kvs := map[string]string{} kvs["ETCD_NAME"] = me.Name kvs["ETCD_INITIAL_CLUSTER_STATE"] = initialClusterState kvs["ETCD_INITIAL_CLUSTER"] = initialCluster return kvs, nil }