// Login on Etcd func EtcdLogin(utente client.User) string { c := GetEtcdAuthClient(utente) kapi := client.NewKeysAPI(c) // Options for loop Node of a cluster _, err := kapi.Get(context.Background(), "/gru/", &client.GetOptions{ Recursive: true, Sort: true, Quorum: false, }) if err != nil { log.Printf("Errore: %v", err) if strings.Contains(err.Error(), ERRORE_SERVER_DOWN) { return ERRORE_SERVER_DOWN } if strings.Contains(err.Error(), ERRORE_LOGIN) { return ERRORE_CREDENZIALI } if !strings.Contains(err.Error(), KEY_NOT_FOUND_LOGIN) { return err.Error() } } return "" }
/* Monitoring changes in etcd server. It designed for run in separate goroutine. */ func etcdMon(etcdRootPath string, config client.Config, bus chan fileChangeEvent, startIndex uint64) { c, err := client.New(config) if err != nil { panic(err) } kapi := client.NewKeysAPI(c) var nextEvent uint64 = startIndex for { response, err := kapi.Watcher(etcdRootPath, &client.WatcherOptions{AfterIndex: nextEvent, Recursive: true}).Next(context.Background()) if err != nil { log.Println(err) time.Sleep(time.Second) continue } nextEvent = response.Index if response.Action == "delete" { bus <- fileChangeEvent{Path: response.Node.Key, IsRemoved: true, IsDir: response.Node.Dir} continue } if response.Node.Dir { bus <- fileChangeEvent{Path: response.Node.Key, IsDir: response.Node.Dir} continue } bus <- fileChangeEvent{Path: response.Node.Key, Content: []byte(response.Node.Value)} } }
func initEtcd() { key := "/" + etcdKey c, err := etcdConnect() if err != nil { log.Fatalf("Can't connect to etcd: %v", err) } keysAPI := client.NewKeysAPI(c) gopts := client.GetOptions{Recursive: false, Sort: false, Quorum: true} r, err := keysAPI.Get(context.Background(), key, &gopts) if err != nil { switch err := err.(type) { case client.Error: if err.Code == 100 { // Not found - create it sopts := client.SetOptions{Dir: true} _, err := keysAPI.Set(context.Background(), key, "", &sopts) if err != nil { log.Fatalf("Error creating etcdKey dir: %v", err) } return } default: log.Fatalf("etcd error: %v", err) } } if !r.Node.Dir { log.Fatalf("Error: etcdKey %q is not a directory", key) } }
func NewEtcdBackend(address string) (Backend, error) { if address == "" { address = "http://127.0.0.1:2379/vault" } url, err := url.Parse(address) if err != nil { return nil, maskAny(err) } path := url.Path // Ensure path is prefixed. if !strings.HasPrefix(path, "/") { path = "/" + path } url.Path = "" endpoint := url.String() c, err := client.New(client.Config{ Endpoints: []string{endpoint}, }) if err != nil { return nil, err } kAPI := client.NewKeysAPI(c) return &etcdBackend{ path: path, kAPI: kAPI, }, nil }
// Add adds a given frontend record with given ID to the list of frontends. // If the given ID already exists, a DuplicateIDError is returned. func (eb *etcdBackend) Add(id string, record api.FrontendRecord) error { if err := validateID(id); err != nil { return maskAny(err) } if err := record.Validate(); err != nil { return maskAny(err) } etcdPath := path.Join(eb.prefix, frontEndPrefix, id) kAPI := client.NewKeysAPI(eb.client) options := &client.SetOptions{ PrevExist: client.PrevNoExist, } rawJSON, err := json.Marshal(record) if err != nil { return maskAny(err) } if _, err := kAPI.Set(context.Background(), etcdPath, string(rawJSON), options); isEtcdError(err, client.ErrorCodeNodeExist) { return maskAny(errgo.WithCausef(nil, api.DuplicateIDError, "Duplicate ID '%s'", id)) } else if err != nil { eb.Logger.Warningf("ETCD error in Add: %#v", err) return maskAny(err) } return nil }
func stress(mb int) error { time.Sleep(5 * time.Second) cfg := client.Config{ Endpoints: []string{"http://localhost:12379", "http://localhost:22379", "http://localhost:32379"}, Transport: client.DefaultTransport, // set timeout per request to fail fast when the target endpoint is unavailable HeaderTimeoutPerRequest: time.Second, } c, err := client.New(cfg) if err != nil { return err } kapi := client.NewKeysAPI(c) for i := 0; i < mb*2; i++ { fmt.Println("stressing", i) k := make([]byte, 100) binary.PutVarint(k, int64(rand.Intn(putSize))) _, err = kapi.Set(context.Background(), string(k), "", nil) if err != nil { if i < 2 { return err } } time.Sleep(500 * time.Millisecond) } return nil }
func TestLeaderLeaseSwapWhileWaiting(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) c, err := testutil.MakeNewEtcdClient() if err != nil { t.Fatal(err) } client := etcdclient.NewKeysAPI(c) key := "/random/key" if _, err := client.Set(context.Background(), key, "holder", &etcdclient.SetOptions{TTL: 10 * time.Second, PrevExist: etcdclient.PrevNoExist}); err != nil { t.Fatal(err) } go func() { time.Sleep(time.Second) if _, err := client.Set(context.Background(), key, "other", &etcdclient.SetOptions{TTL: 10 * time.Second}); err != nil { t.Fatal(err) } glog.Infof("Changed key ownership") }() lease := leaderlease.NewEtcd(c, key, "other", 10) ch := make(chan error, 1) go lease.AcquireAndHold(ch) <-ch glog.Infof("Lease acquired") lease.Release() if err, ok := <-ch; err == nil || !ok || !strings.Contains(err.Error(), "the lease has been lost") { t.Errorf("Expected error and open channel when lease was swapped: %v %t", err, ok) } <-ch glog.Infof("Lease gone") }
func main() { logger := log.New(os.Stderr, "server", log.LstdFlags) if err := serverFlags.Parse(os.Args[1:]); err != nil { logger.Fatalf("%v", err) } client, err := etcd.New(etcd.Config{Endpoints: *etcdEndpoints}) if err != nil { logger.Fatalf("Failed setting up etcd %v", err) } w, err := watcher.New(serverFlags, etcd.NewKeysAPI(client), *etcdFlagzPath, logger) if err != nil { logger.Fatalf("Failed setting up watcher %v", err) } err = w.Initialize() if err != nil { logger.Fatalf("Failed initializing watcher %v", err) } w.Start() logger.Printf("etcd flag value watching initialized") flagzEndpoint := flagz.NewStatusEndpoint(serverFlags) http.HandleFunc("/debug/flagz", flagzEndpoint.ListFlags) http.HandleFunc("/", handleDefaultPage) addr := fmt.Sprintf("%s:%d", *listenHost, *listenPort) logger.Printf("Serving at: %v", addr) if err := http.ListenAndServe(addr, http.DefaultServeMux); err != nil { logger.Fatalf("Failed serving: %v", err) } logger.Printf("Done, bye.") }
// NewEtcdConfig creates a new service discovery backend for etcd func NewEtcdConfig(config map[string]interface{}) Etcd { etcd := Etcd{ Prefix: "/containerbuddy", } etcdConfig := client.Config{} switch endpoints := config["endpoints"].(type) { case string: etcdConfig.Endpoints = []string{endpoints} case []string: etcdConfig.Endpoints = endpoints default: log.Fatal("Must provide etcd endpoints") } prefix, ok := config["prefix"].(string) if ok { etcd.Prefix = prefix } etcdClient, err := client.New(etcdConfig) if err != nil { log.Fatal(err) } etcd.Client = etcdClient etcd.API = client.NewKeysAPI(etcdClient) return etcd }
// GetV2 gets the value to the key using V2 API. func (c *Cluster) GetV2(w io.Writer, key []byte) error { endpoints := []string{} for _, nd := range c.NameToNode { for v := range nd.Flags.ListenClientURLs { endpoints = append(endpoints, v) } } cfg := client.Config{ Endpoints: endpoints, Transport: client.DefaultTransport, HeaderTimeoutPerRequest: time.Second, // SelectionMode: client.EndpointSelectionPrioritizeLeader, } ct, err := client.New(cfg) if err != nil { return err } kapi := client.NewKeysAPI(ct) ts := time.Now() resp, err := kapi.Get(context.Background(), string(key), nil) if err != nil { return err } fmt.Fprintf(w, "[GetV2] Done! Took %v for %s/%s.\n", time.Since(ts), key, resp.Node.Value) return nil }
func NewKeysAPI(cfg etcd.Config) (etcd.KeysAPI, error) { eCli, err := etcd.New(cfg) if err != nil { return nil, err } return etcd.NewKeysAPI(eCli), nil }
func main() { myFlagSet.Parse(os.Args[1:]) logger := log.New(os.Stderr, "wr ", log.LstdFlags) client, err := etcd.New(etcd.Config{Endpoints: []string{"http://localhost:2379"}}) if err != nil { logger.Fatalf("Failed setting up %v", err) } w, err := watcher.New(myFlagSet, etcd.NewKeysAPI(client), "/example/flagz", logger) if err != nil { logger.Fatalf("Failed setting up %v", err) } err = w.Initialize() if err != nil { logger.Fatalf("Failed setting up %v", err) } w.Start() for true { logger.Printf("staticint: %v dynint: %v dynstring: %v", *staticInt, dynInt.Get(), dynStr.Get()) time.Sleep(1500 * time.Millisecond) } }
func NewNode(stop chan bool) *Node { n := new(Node) n.stop = stop n.stopBoard = make(chan bool, 2) n.stopThread = make(chan bool, 2) n.stopPost = make(chan bool, 2) n.stopFile = make(chan bool, 2) n.Stats = NewNodeStats() n.Config = parseFlags() n.Storage = fourchan.NewStorage(n.Config.CassKeyspace, n.Config.CassEndpoints...) cfg := etcd.Config{ Endpoints: n.Config.EtcdEndpoints, Transport: etcd.DefaultTransport, HeaderTimeoutPerRequest: 3 * time.Second, } c, err := etcd.New(cfg) if err != nil { log.Fatal("Failed to connected to etcd: ", err) } n.Keys = etcd.NewKeysAPI(c) n.Closed = false // TODO these chan sizes are rather arbitrary... n.CBoard = make(chan *fourchan.Board, numBoardRoutines) n.CThread = make(chan *fourchan.Thread, numThreadRoutines) n.CPost = make(chan *fourchan.Post, numPostRoutines) n.CFile = make(chan *fourchan.File, numFileRoutines) n.Files = make(map[int]string) return n }
func UpdateConfig(c client.Client, cluster, confType, serviceName, confFile string) (string, string) { kapi := client.NewKeysAPI(c) key := "" successMessage := "" switch confType { case "agent": key = "/gru/" + cluster + "/config" successMessage = ADD_AGENT case "service": key = "/gru/" + cluster + "/services/" + serviceName successMessage = ADD_SERVICE case "policy": key = "/gru/" + cluster + "/policy" successMessage = ADD_POLICY case "analytic": analyticsName := serviceName key = "/gru/" + cluster + "/analytics/" + analyticsName successMessage = ADD_ANALYTICS default: log.Fatal("Unrecognized configuration type") } resp, err := kapi.Update(context.Background(), key, confFile) if err != nil { log.Printf(err.Error()) return "", err.Error() } // print common key info log.Printf("Set is done. Metadata is %q\n", resp) return successMessage, "" }
// watchBackends monitors the registrator namespace for // changes affecting services which are monitored. It // calls update if there are changes detected. func watchBackends(ctx context.Context) { k := client.NewKeysAPI(etcd) watcher := k.Watcher(registratorNamespace, &client.WatcherOptions{ Recursive: true, }) // Process changes for { resp, err := watcher.Next(ctx) if err != nil { fmt.Println("Error watching backends", err.Error()) continue } // Ignore read-only events if resp.Action == "get" { continue } // Find the service of the modified key serviceName := serviceFromRegistratorKey(resp.Node.Key) if serviceName == "" { continue } // Update only if this service is one we are watching for _, s := range services { if serviceName == s.Name { Update() break } } } }
func Conn() { cfg := client.Config{ Endpoints: []string{"http://127.0.0.1:2379/", "http://127.0.0.1:4001"}, Transport: client.DefaultTransport, HeaderTimeoutPerRequest: time.Second, } c, err := client.New(cfg) if err != nil { log.Fatal(err) } kapi := client.NewKeysAPI(c) log.Print("Setting '/foo' key with 'bar' value") resp, err := kapi.Set(context.Background(), "/foo", "bar", nil) if err != nil { log.Fatal(err) } else { log.Printf("Set is done.Metadata is %q\n", resp) } log.Print("Getting '/foo' key value") resp, err = kapi.Get(context.Background(), "/foo", nil) if err != nil { log.Fatal(err) } else { log.Printf("Get is done,Metadata is %q\n", resp) log.Printf("%q key has %q value\n", resp.Node.Key, resp.Node.Value) } }
func (s *Store) New(c config.Config) *Store { // Open an etcd client var endpoints []string endpoints = append(endpoints, fmt.Sprintf("http://%v:%v", c.Etcd.Hostname, c.Etcd.Port)) ec := client.Config{ Endpoints: endpoints, Transport: client.DefaultTransport, } e, err := client.New(ec) if err != nil { log.Fatalln("Could not connect to etcd:", err) } // Create a new KeysAPI for etcd k := client.NewKeysAPI(e) ns := &Store{ e: e, c: c, k: k, basePath: fmt.Sprint(c.Etcd.BasePath, "/vips/"), } return ns }
func TestBasic(t *testing.T) { cfg := client.Config{ Endpoints: []string{"http://127.0.0.1:4001"}, Transport: client.DefaultTransport, HeaderTimeoutPerRequest: time.Second, } c, err := client.New(cfg) if err != nil { log.Panicln(err) } kapi := client.NewKeysAPI(c) client := EtcdReigistryClient{ EtcdRegistryConfig{ ServiceName: "test", InstanceName: "test1", BaseURL: "127.0.0.1:8080", }, kapi, } client.Register() response, _ := client.ServicesByName("test") if len(response) == 0 { t.Error("No service registered") } client.Unregister() response, _ = client.ServicesByName("test") if len(response) != 0 { t.Error("Service not unregistered") } }
func getFleetRegistryClient(fleetEndpoints []string) (fleetClient.API, error) { var dial func(string, string) (net.Conn, error) tlsConfig, err := fleetPkg.ReadTLSConfigFiles("", "", "") if err != nil { return nil, err } trans := &http.Transport{ Dial: dial, TLSClientConfig: tlsConfig, } timeout := 3 * time.Second eCfg := etcd.Config{ Endpoints: fleetEndpoints, Transport: trans, } eClient, err := etcd.New(eCfg) if err != nil { return nil, err } kAPI := etcd.NewKeysAPI(eClient) reg := registry.NewEtcdRegistry(kAPI, registry.DefaultKeyPrefix, timeout) return &fleetClient.RegistryClient{Registry: reg}, nil }
func TestKeepAlive(t *testing.T) { cfg := client.Config{ Endpoints: []string{"http://127.0.0.1:2379"}, Transport: client.DefaultTransport, HeaderTimeoutPerRequest: time.Second, } c, err := client.New(cfg) if err != nil { log.Panicln(err) } kapi := client.NewKeysAPI(c) client := EtcdReigistryClient{ EtcdRegistryConfig{ ServiceName: "test", InstanceName: "test1", BaseURL: "127.0.0.1:8080", }, kapi, } client.Register() time.Sleep(50 * time.Second) response, _ := client.ServicesByName("test") log.Println(response) // Ahoj }
func syncToEtcd(ctx context.Context, cfg *config.Config) error { log.Log(ctx).Debug("Connecting to etcd") etcdCfg := client.Config{ Endpoints: strings.Split(etcdEndpoints, ","), } c, err := client.New(etcdCfg) if err != nil { return err } root := "/quicklog/" + instanceName kapi := client.NewKeysAPI(c) input := cfg.Input inputConfig, err := json.Marshal(input.Config) if err != nil { log.Log(ctx).Error("Error converting input config to JSON data", "error", err) return err } output := cfg.Output outputConfig, err := json.Marshal(output.Config) if err != nil { log.Log(ctx).Error("Error converting output config to JSON data", "error", err) return err } filters := cfg.Filters kapi.Set(ctx, root+"/input/driver", input.Driver, nil) kapi.Set(ctx, root+"/input/parser", input.Parser, nil) kapi.Set(ctx, root+"/input/config", string(inputConfig), nil) kapi.Set(ctx, root+"/output/driver", output.Driver, nil) kapi.Set(ctx, root+"/output/config", string(outputConfig), nil) // clear all the filters before re-creating them kapi.Delete(ctx, root+"/filters", &client.DeleteOptions{Recursive: true, Dir: true}) // filters must exist even if empty kapi.Set(ctx, root+"/filters", "", &client.SetOptions{Dir: true}) for idx, filter := range filters { filterConfig, err := json.Marshal(filter.Config) if err != nil { log.Log(ctx).Error("Error converting filter config to JSON data", "error", err) return err } kapi.Set(ctx, root+"/filters/"+strconv.Itoa(idx)+"/driver", filter.Driver, nil) kapi.Set(ctx, root+"/filters/"+strconv.Itoa(idx)+"/config", string(filterConfig), nil) } kapi.Set(ctx, root+"/reload", "1", nil) return nil }
// TestV2NoRetryEOF tests destructive api calls won't retry on a disconnection. func TestV2NoRetryEOF(t *testing.T) { defer testutil.AfterTest(t) // generate an EOF response; specify address so appears first in sorted ep list lEOF := integration.NewListenerWithAddr(t, fmt.Sprintf("eof:123.%d.sock", os.Getpid())) defer lEOF.Close() tries := uint32(0) go func() { for { conn, err := lEOF.Accept() if err != nil { return } atomic.AddUint32(&tries, 1) conn.Close() } }() eofURL := integration.UrlScheme + "://" + lEOF.Addr().String() cli := integration.MustNewHTTPClient(t, []string{eofURL, eofURL}, nil) kapi := client.NewKeysAPI(cli) for i, f := range noRetryList(kapi) { startTries := atomic.LoadUint32(&tries) if err := f(); err == nil { t.Errorf("#%d: expected EOF error, got nil", i) } endTries := atomic.LoadUint32(&tries) if startTries+1 != endTries { t.Errorf("#%d: expected 1 try, got %d", i, endTries-startTries) } } }
// Get returns the frontend record for the given id. // If the ID is not found, an IDNotFoundError is returned. func (eb *etcdBackend) Get(id string) (api.FrontendRecord, error) { if err := validateID(id); err != nil { return api.FrontendRecord{}, maskAny(err) } etcdPath := path.Join(eb.prefix, frontEndPrefix, id) kAPI := client.NewKeysAPI(eb.client) options := &client.GetOptions{ Recursive: false, Sort: false, } resp, err := kAPI.Get(context.Background(), etcdPath, options) if isEtcdError(err, client.ErrorCodeKeyNotFound) { return api.FrontendRecord{}, maskAny(errgo.WithCausef(nil, api.IDNotFoundError, "ID '%s' not found", id)) } if err != nil { eb.Logger.Warningf("ETCD error in Get: %#v", err) return api.FrontendRecord{}, maskAny(err) } if resp.Node == nil { return api.FrontendRecord{}, maskAny(errgo.WithCausef(nil, api.IDNotFoundError, "ID '%s' not found", id)) } rawJSON := resp.Node.Value record := api.FrontendRecord{} if err := json.Unmarshal([]byte(rawJSON), &record); err != nil { return api.FrontendRecord{}, maskAny(fmt.Errorf("Cannot unmarshal registration of %s", id)) } return record, nil }
// TestV2NoRetryNoLeader tests destructive api calls won't retry if given an error code. func TestV2NoRetryNoLeader(t *testing.T) { defer testutil.AfterTest(t) lHttp := integration.NewListenerWithAddr(t, fmt.Sprintf("errHttp:123.%d.sock", os.Getpid())) eh := &errHandler{errCode: http.StatusServiceUnavailable} srv := httptest.NewUnstartedServer(eh) defer lHttp.Close() defer srv.Close() srv.Listener = lHttp go srv.Start() lHttpURL := integration.UrlScheme + "://" + lHttp.Addr().String() cli := integration.MustNewHTTPClient(t, []string{lHttpURL, lHttpURL}, nil) kapi := client.NewKeysAPI(cli) // test error code for i, f := range noRetryList(kapi) { reqs := eh.reqs if err := f(); err == nil || !strings.Contains(err.Error(), "no leader") { t.Errorf("#%d: expected \"no leader\", got %v", i, err) } if eh.reqs != reqs+1 { t.Errorf("#%d: expected 1 request, got %d", i, eh.reqs-reqs) } } }
// All returns a map of all known frontend records mapped by their ID. func (eb *etcdBackend) All() (map[string]api.FrontendRecord, error) { etcdPath := path.Join(eb.prefix, frontEndPrefix) kAPI := client.NewKeysAPI(eb.client) options := &client.GetOptions{ Recursive: false, Sort: false, } result := make(map[string]api.FrontendRecord) resp, err := kAPI.Get(context.Background(), etcdPath, options) if isEtcdError(err, client.ErrorCodeKeyNotFound) { return result, nil } if err != nil { eb.Logger.Warningf("ETCD error in All: %#v", err) return nil, maskAny(err) } if resp.Node == nil { return result, nil } for _, frontEndNode := range resp.Node.Nodes { id := path.Base(frontEndNode.Key) rawJSON := frontEndNode.Value record := api.FrontendRecord{} if err := json.Unmarshal([]byte(rawJSON), &record); err != nil { eb.Logger.Errorf("Cannot unmarshal registration of %s", frontEndNode.Key) continue } result[id] = record } return result, nil }
func (etc *etcd) Init() error { var err error if len(etc.Endpoints) == 0 { return e.New("no end points") } cfg := client.Config{ Endpoints: etc.Endpoints, //Transport: http.DefaultTransport, } c, err := client.New(cfg) if err != nil { return e.Forward(err) } etc.kapi = client.NewKeysAPI(c) if etc.SecKeyRing == "" { return nil } kr, err := os.Open(etc.SecKeyRing) if err != nil { return e.Forward(err) } defer kr.Close() etc.cm, err = config.NewEtcdConfigManager(etc.Endpoints, kr) if err != nil { return e.Forward(err) } return nil }
func TestGet(t *testing.T) { client := framework.NewEtcdClient() keysAPI := etcd.NewKeysAPI(client) etcdStorage := etcdstorage.NewEtcdStorage(client, testapi.Default.Codec(), "", false) ctx := context.TODO() framework.WithEtcdKey(func(key string) { testObject := api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "foo"}} coded, err := runtime.Encode(testapi.Default.Codec(), &testObject) if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = keysAPI.Set(ctx, key, string(coded), nil) if err != nil { t.Fatalf("unexpected error: %v", err) } result := api.ServiceAccount{} if err := etcdStorage.Get(ctx, key, &result, false); err != nil { t.Fatalf("unexpected error: %v", err) } // Propagate ResourceVersion (it is set automatically). testObject.ObjectMeta.ResourceVersion = result.ObjectMeta.ResourceVersion if !api.Semantic.DeepEqual(testObject, result) { t.Errorf("expected: %#v got: %#v", testObject, result) } }) }
// Load reads all the services from etcd func Load() (changed bool, err error) { // Get a keysAPI instance k := client.NewKeysAPI(etcd) // Get the service keys resp, err := k.Get(context.Background(), serviceNamespace, &client.GetOptions{ Recursive: true, Sort: true, Quorum: false, }) if err != nil { return false, err } // Parse each service for _, i := range resp.Node.Nodes { s, err := ParseServiceNode(i) if err != nil { continue } // If there are no changes; don't update the reference if old, ok := services[s.Name]; ok { if old.Equals(s) { continue } } // Add/update the service services[s.Name] = s changed = true } return changed, nil }
// Creates a new etcd minion func NewEtcdMinion(name string, cfg etcdclient.Config) Minion { c, err := etcdclient.New(cfg) if err != nil { log.Fatal(err) } kapi := etcdclient.NewKeysAPI(c) id := utils.GenerateUUID(name) rootDir := filepath.Join(EtcdMinionSpace, id.String()) queueDir := filepath.Join(rootDir, "queue") classifierDir := filepath.Join(rootDir, "classifier") logDir := filepath.Join(rootDir, "log") taskQueue := make(chan *task.Task) done := make(chan struct{}) m := &etcdMinion{ name: name, rootDir: rootDir, queueDir: queueDir, classifierDir: classifierDir, logDir: logDir, id: id, kapi: kapi, taskQueue: taskQueue, done: done, } return m }
// Stores the certificate in the authorization pending subtree func (ck *CertKit) SavePending(cert *x509.Certificate) error { var err error var CertKey string var Pem string var tgtpath string CertKey = certKey(cert) Goose.Auth.Logf(3, "User certificate of %s not authorized", CertKey) Goose.Auth.Logf(6, "Certificate is %#v", cert) tgtpath = ck.Etcdkey + "/pending/" + CertKey _, err = etcd.NewKeysAPI(ck.Etcdcli).Set(context.Background(), tgtpath, "", &etcd.SetOptions{Dir: true}) if err != nil { Goose.Auth.Logf(1, "Error creating diretory for pending certificate (%s): %s", tgtpath, err) return err } Pem = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})) Goose.Auth.Logf(6, "Pem Certificate is %#v", Pem) err = etcdconfig.SetKey(ck.Etcdcli, tgtpath+"/cert", Pem) if err != nil { Goose.Auth.Logf(1, "Error saving pending certificate (%s): %s", tgtpath, err) return err } return err }