Example #1
0
// Create etcd directory structure from a map, slice or struct.
func Create(kapi client.KeysAPI, path string, val reflect.Value) error {
	switch val.Kind() {
	case reflect.Ptr:
		orig := val.Elem()
		if !orig.IsValid() {
			return nil
		}
		if err := Create(kapi, path, orig); err != nil {
			return err
		}
	case reflect.Interface:
		orig := val.Elem()
		if err := Create(kapi, path, orig); err != nil {
			return err
		}
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			t := val.Type().Field(i)
			k := t.Tag.Get("etcd")
			if err := Create(kapi, path+"/"+k, val.Field(i)); err != nil {
				return err
			}
		}
	case reflect.Map:
		if strings.HasPrefix(pathx.Base(path), "_") {
			log.Printf("create hidden directory in etcd: %s", path)
		}
		for _, k := range val.MapKeys() {
			v := val.MapIndex(k)
			if err := Create(kapi, path+"/"+k.String(), v); err != nil {
				return err
			}
		}
	case reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			Create(kapi, fmt.Sprintf("%s/%d", path, i), val.Index(i))
		}
	case reflect.String:
		if strings.HasPrefix(pathx.Base(path), "_") {
			log.Printf("set hidden key in etcd: %s", path)
		}
		_, err := kapi.Set(context.TODO(), path, val.String(), nil)
		if err != nil {
			return err
		}
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
		if strings.HasPrefix(pathx.Base(path), "_") {
			log.Printf("set hidden key in etcd: %s", path)
		}
		_, err := kapi.Set(context.TODO(), path, fmt.Sprintf("%v", val.Interface()), nil)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("unsupported type: %s for path: %s", val.Kind(), path)
	}

	return nil
}
Example #2
0
// mkCommandFunc executes the "mk" command.
func mkCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")

	ctx, cancel := contextWithTotalTimeout(c)
	// Since PrevNoExist means that the Node must not exist previously,
	// this Set method always creates a new key. Therefore, mk command
	// succeeds only if the key did not previously exist, and the command
	// prevents one from overwriting values accidentally.
	resp, err := ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevNoExist})
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}

	printResponseKey(resp, c.GlobalString("output"))
}
Example #3
0
func mustCreateServiceDirectory(ctx context.Context, kApi etcd.KeysAPI, basepath string) {
	myContext, myCancel := context.WithTimeout(ctx, DefaultTimeout)
	defer myCancel()

	_, err := kApi.Set(myContext, basepath, "", &etcd.SetOptions{Dir: true, PrevExist: etcd.PrevNoExist})
	log.WithField("error", err).Warn("error creating servicedef directory")
}
Example #4
0
func doServerBeat(kapi client.KeysAPI) {
	var key = runningbase + *serverbeatname

	myuuid := uuid.NewV4()
	uuidstring := myuuid.String()

	fmt.Println("Badum")
	_, err := kapi.Set(context.TODO(), key, uuidstring, &client.SetOptions{PrevExist: client.PrevNoExist, TTL: time.Second * 60})
	if err != nil {
		log.Fatal(err)
	}

	running := true
	counter := *serverbeatcount

	for running {
		time.Sleep(time.Second * time.Duration(*serverbeattime))
		fmt.Println("Badum")
		_, err := kapi.Set(context.TODO(), key, uuidstring, &client.SetOptions{PrevExist: client.PrevExist, TTL: time.Second * 60, PrevValue: uuidstring})
		if err != nil {
			log.Fatal(err)
		}
		if *serverbeatcount != 0 {
			counter = counter - 1
			if counter == 0 {
				running = false
			}
		}
	}

	_, err = kapi.Delete(context.TODO(), key, &client.DeleteOptions{PrevValue: uuidstring})
	if err != nil {
		log.Fatal(err)
	}
}
Example #5
0
// mkCommandFunc executes the "mk" command.
func mkCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")
	inorder := c.Bool("in-order")

	var resp *client.Response
	ctx, cancel := contextWithTotalTimeout(c)
	if !inorder {
		// Since PrevNoExist means that the Node must not exist previously,
		// this Set method always creates a new key. Therefore, mk command
		// succeeds only if the key did not previously exist, and the command
		// prevents one from overwriting values accidentally.
		resp, err = ki.Set(ctx, key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevNoExist})
	} else {
		// If in-order flag is specified then create an inorder key under
		// the directory identified by the key argument.
		resp, err = ki.CreateInOrder(ctx, key, value, &client.CreateInOrderOptions{TTL: time.Duration(ttl) * time.Second})
	}
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}

	printResponseKey(resp, c.GlobalString("output"))
}
Example #6
0
func Store(api etcd.KeysAPI, path string, ttl time.Duration) frameworkid.Storage {
	// TODO(jdef) validate Config
	return &storage{
		LookupFunc: func(ctx context.Context) (string, error) {
			if response, err := api.Get(ctx, path, nil); err != nil {
				if !etcdutil.IsEtcdNotFound(err) {
					return "", fmt.Errorf("unexpected failure attempting to load framework ID from etcd: %v", err)
				}
			} else {
				return response.Node.Value, nil
			}
			return "", nil
		},
		RemoveFunc: func(ctx context.Context) (err error) {
			if _, err = api.Delete(ctx, path, &etcd.DeleteOptions{Recursive: true}); err != nil {
				if !etcdutil.IsEtcdNotFound(err) {
					return fmt.Errorf("failed to delete framework ID from etcd: %v", err)
				}
			}
			return
		},
		StoreFunc: func(ctx context.Context, id string) (err error) {
			_, err = api.Set(ctx, path, id, &etcd.SetOptions{TTL: ttl})
			return
		},
	}
}
Example #7
0
func setJob(ctx context.Context, kapi client.KeysAPI, j *job) error {
	errChan := make(chan error)
	done := make(chan struct{})
	go func() {
		buf := new(bytes.Buffer)
		if err := json.NewEncoder(buf).Encode(j); err != nil {
			errChan <- err
			return
		}
		value := buf.String()
		fmt.Println("setJob:", value)
		opts := &client.SetOptions{}
		if _, err := kapi.Set(ctx, j.ETCDKey, value, opts); err != nil {
			errChan <- err
			return
		}
		done <- struct{}{}
	}()
	select {
	case <-done:
		return nil

	case v := <-errChan:
		return v

	case <-ctx.Done():
		return ctx.Err()
	}
}
Example #8
0
func sync(kapi client.KeysAPI, syncChannel chan blocker.ControlMsg) {
	for {
		msg := <-syncChannel
		start := time.Now()
		var folder string
		if msg.Ip.To4() == nil {
			folder = "dblock6/"
		} else {
			folder = "dblock/"
		}
		_, err := kapi.Set(context.Background(), folder+msg.Ip.String(), "0", &client.SetOptions{TTL: 60 * time.Second, PrevExist: client.PrevNoExist})
		if err != nil {
			if err.(client.Error).Code == client.ErrorCodeNodeExist {
				log.Print("Block already existed, not adding again")
			} else {
				log.Fatal(err)
			}
		} else {
			// print common key info
			log.Println("[sync]\tAdded block: " + msg.Ip.String())
		}
		elapsed := time.Since(start)
		log.Printf("sync took %s", elapsed)
		sync_timing.Observe(elapsed.Seconds())
	}
}
Example #9
0
func NewPubSubTopicByKey(ctx context.Context, keyid string, ttl time.Duration, kapi etcdc.KeysAPI) (*EtcdPubSubTopic, error) {
	_, err := kapi.Get(ctx, keyid, nil)
	if IsKeyNotFound(err) {
		opt := &etcdc.SetOptions{PrevExist: etcdc.PrevNoExist, TTL: ttl, Dir: true}
		_, err = kapi.Set(ctx, keyid, "", opt)
		if err != nil && !IsCompareAndSwapFailure(err) && !IsNodeExists(err) {
			return nil, err
		}
	} else if err != nil {
		return nil, err
	}

	keepalive, err := NewNodeKeepAlive(keyid, ttl, kapi)
	if err != nil {
		return nil, err
	}

	dlog("signal: new signal(pub/sub) for %v, ttl:%v", keyid, ttl)

	return &EtcdPubSubTopic{
		ctx:       ctx,
		keyid:     keyid,
		kapi:      kapi,
		ttl:       ttl,
		keepalive: keepalive,
		stop:      make(chan bool),
	}, nil
}
func setValues(kapi client.KeysAPI, kvs map[string]string) error {
	for k, v := range kvs {
		if _, err := kapi.Set(context.Background(), k, v, &client.SetOptions{}); err != nil {
			return err
		}
	}
	return nil
}
Example #11
0
func importFunc(dir string, file string, f iodatafmt.DataFmt, replace bool, yes bool, e Etcdtool, c *cli.Context, ki client.KeysAPI) {
	// Check if dir exists and is a directory.
	exists, err := dirExists(dir, c, ki)
	if err != nil {
		fatalf("Specified dir doesn't exist: %s", dir)
	}

	if exists {
		exist, err := isDir(dir, c, ki)
		if err != nil {
			fatal(err.Error())
		}

		if exist {
			fatalf("Specified dir is not a directory: %s", dir)
		}
	}

	// Load file.
	m, err := iodatafmt.Load(file, f)
	if err != nil {
		fatal(err.Error())
	}

	// Validate data.
	if c.Bool("validate") {
		validateFunc(e, dir, m)
	}

	if exists {
		if replace {
			if !askYesNo(fmt.Sprintf("Do you want to overwrite data in directory: %s", dir)) {
				os.Exit(1)
			}

			// Delete dir.
			if _, err = ki.Delete(context.TODO(), dir, &client.DeleteOptions{Recursive: true}); err != nil {
				fatal(err.Error())
			}
		} else {
			if !yes {
				if !askYesNo(fmt.Sprintf("Do you want to overwrite data in directory: %s", dir)) {
					os.Exit(1)
				}
			}
		}
	} else {
		// Create dir.
		if _, err := ki.Set(context.TODO(), dir, "", &client.SetOptions{Dir: true}); err != nil {
			fatal(err.Error())
		}
	}

	// Import data.
	if err = etcdmap.Create(ki, dir, reflect.ValueOf(m)); err != nil {
		fatal(err.Error())
	}
}
Example #12
0
File: etcd.go Project: helgi/pkg
func setHostPort(k client.KeysAPI, base, host, port string, ttl time.Duration) error {
	o := client.SetOptions{TTL: ttl}
	if _, err := k.Set(dctx(), base+"/host", host, &o); err != nil {
		return err
	}
	if _, err := k.Set(dctx(), base+"/port", port, &o); err != nil {
		return err
	}
	return nil
}
Example #13
0
func doConfig(kapi client.KeysAPI) {
	var key = configbase + *configserver + "/" + *configvar

	resp, err := kapi.Set(context.TODO(), key, *configval, nil)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(resp.Action + " " + resp.Node.Key + " to " + resp.Node.Value)
}
Example #14
0
func (s *Server) put(ctx context.Context, client etcd.KeysAPI, vulcanpath, servicename, instancename string) error {
	myContext, cancel := context.WithTimeout(ctx, DefaultTimeout)
	defer cancel()

	key := fmt.Sprintf(serverPathPattern, vulcanpath, servicename, instancename)
	se, err := json.Marshal(s)
	if err != nil {
		return err
	}

	_, err = client.Set(myContext, key, fmt.Sprintf("%s", se), &etcd.SetOptions{TTL: serverTTL, PrevExist: etcd.PrevIgnore})
	return err
}
Example #15
0
func (b *Backend) put(ctx context.Context, client etcd.KeysAPI, vulcanpath, servicename string) error {
	myContext, cancel := context.WithTimeout(ctx, DefaultTimeout)
	defer cancel()

	key := fmt.Sprintf(backendPathPattern, vulcanpath, servicename)
	be, err := json.Marshal(b)
	if err != nil {
		return err
	}

	_, err = client.Set(myContext, key, fmt.Sprintf("%s", be), &etcd.SetOptions{TTL: 0, PrevExist: etcd.PrevIgnore})
	return err
}
Example #16
0
// createDir will create path if it doesn't exist and is not root.
func createDir(kapi client.KeysAPI, path string) error {
	if path != "/" {
		if _, err := kapi.Set(context.TODO(), path, "", &client.SetOptions{Dir: true, PrevExist: client.PrevNoExist}); err != nil {
			if etcdErr, ok := err.(client.Error); ok {
				if etcdErr.Code == client.ErrorCodeNodeExist {
					return nil
				}
			}
			return err
		}
	}
	return nil
}
Example #17
0
func noRetryList(kapi client.KeysAPI) []func() error {
	return []func() error{
		func() error {
			opts := &client.SetOptions{PrevExist: client.PrevNoExist}
			_, err := kapi.Set(context.Background(), "/setkey", "bar", opts)
			return err
		},
		func() error {
			_, err := kapi.Delete(context.Background(), "/delkey", nil)
			return err
		},
	}
}
Example #18
0
// mkdirCommandFunc executes the "mkdir" command.
func mkdirCommandFunc(c *cli.Context, ki client.KeysAPI, prevExist client.PrevExistType) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}

	key := c.Args()[0]
	ttl := c.Int("ttl")

	_, err := ki.Set(context.TODO(), key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: prevExist})
	if err != nil {
		handleError(ExitServerError, err)
	}
}
Example #19
0
// updatedirCommandFunc executes the "updatedir" command.
func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	ttl := c.Int("ttl")
	ctx, cancel := contextWithTotalTimeout(c)
	_, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}
}
Example #20
0
func runSet(ki client.KeysAPI, setc chan set, wg *sync.WaitGroup) {
	for s := range setc {
		log.Println("copying key:", s.key)
		if s.ttl != 0 && s.ttl < 300 {
			log.Printf("extending key %s's ttl to 300 seconds", s.key)
			s.ttl = 5 * 60
		}
		_, err := ki.Set(context.TODO(), s.key, s.value, &client.SetOptions{TTL: time.Duration(s.ttl) * time.Second})
		if err != nil {
			log.Fatalf("failed to copy key: %v\n", err)
		}
	}
	wg.Done()
}
Example #21
0
// updatedirCommandFunc executes the "updatedir" command.
func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")

	_, err = ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
	if err != nil {
		handleError(ExitServerError, err)
	}
}
Example #22
0
func (s *v2Stresser) run(ctx context.Context, kv clientV2.KeysAPI) {
	for {
		if err := s.rateLimiter.Wait(ctx); err == context.Canceled {
			return
		}
		setctx, setcancel := context.WithTimeout(ctx, clientV2.DefaultRequestTimeout)
		key := fmt.Sprintf("foo%016x", rand.Intn(s.keySuffixRange))
		_, err := kv.Set(setctx, key, string(randBytes(s.keySize)), nil)
		if err == nil {
			atomic.AddInt64(&s.atomicModifiedKey, 1)
		}
		setcancel()
		if err == context.Canceled {
			return
		}
	}
}
Example #23
0
// mkdirCommandFunc executes the "mkdir" command.
func mkdirCommandFunc(c *cli.Context, ki client.KeysAPI, prevExist client.PrevExistType) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}

	key := c.Args()[0]
	ttl := c.Int("ttl")

	ctx, cancel := contextWithTotalTimeout(c)
	resp, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: prevExist})
	cancel()
	if err != nil {
		handleError(ExitServerError, err)
	}
	if c.GlobalString("output") != "simple" {
		printResponseKey(resp, c.GlobalString("output"))
	}
}
// NewRuntimeConfiguration initialize etcd tree, do some validations, and returns a ready RuntimeConfiguration
func NewRuntimeConfiguration(dataSource etcd.KeysAPI, etcdClient etcd.Client, etcdDir string, workspacePath string) (*RuntimeConfiguration, error) {
	data, err := ioutil.ReadFile(filepath.Join(workspacePath, "initial.yaml"))
	if err != nil {
		return nil, fmt.Errorf("Error while trying to read initial data: %s", err)
	}

	var iVals initialValues
	err = yaml.Unmarshal(data, &iVals)
	if err != nil {
		return nil, fmt.Errorf("Error while reading initial data: %s", err)
	}
	if iVals.CoreOSVersion == "" {
		return nil, errors.New("A valid initial CoreOS version is required in initial data")
	}

	fmt.Printf("Initial Values:  CoreOSVersion=%s\n", iVals.CoreOSVersion)

	instance := &RuntimeConfiguration{
		DataSource:           dataSource,
		EtcdClient:           etcdClient,
		EtcdDir:              etcdDir,
		WorkspacePath:        workspacePath,
		initialCoreOSVersion: iVals.CoreOSVersion,
	}

	_, err = instance.GetCoreOSVersion()
	if err != nil {
		etcdError, found := err.(etcd.Error)
		if found && etcdError.Code == etcd.ErrorCodeKeyNotFound {
			// Initializing
			ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
			defer cancel()
			_, err = dataSource.Set(ctx, path.Join(etcdDir, "/coreos-version"), iVals.CoreOSVersion, nil)
			if err != nil {
				return nil, fmt.Errorf("Error while initializing etcd tree: %s", err)
			}
			fmt.Printf("Initialized etcd tree (%s)", etcdDir)
		} else {
			return nil, fmt.Errorf("Error while checking GetCoreOSVersion: %s", err)
		}
	}

	return instance, nil
}
Example #25
0
// updateCommandFunc executes the "update" command.
func updateCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")

	resp, err := ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevExist: client.PrevExist})
	if err != nil {
		handleError(ExitServerError, err)
	}

	printResponseKey(resp, c.GlobalString("output"))
}
Example #26
0
func makeTimestamps(kAPI client.KeysAPI, dir string, dry bool) {
	for {
		for {
			if dry {
				break
			}
			keyshort := strconv.FormatInt(time.Now().UnixNano(), 16)
			key := fmt.Sprintf("%016s", keyshort)
			_, err := kAPI.Set(context.TODO(), path.Join(dir, key), "timestamp", &client.SetOptions{})
			if err == nil {
				break
			}
		}

		fmt.Printf("made a timestamp at %v, sleeping for a day to make another one\n", time.Now())

		time.Sleep(day)
	}
}
Example #27
0
// put updates/creates the job.
func put(kapi client.KeysAPI, job *Job) error {
	job.FinishedTimestamp = time.Now().UTC().String()[:19]
	buf := new(bytes.Buffer)
	if err := json.NewEncoder(buf).Encode(job); err != nil {
		return err
	}
	value := buf.String()
	if _, err := kapi.Set(context.Background(), ctx, job.ETCDKey, value, nil); err != nil {
		if err == context.Canceled {
			return fmt.Errorf("ctx is canceled by another routine")
		} else if err == context.DeadlineExceeded {
			return fmt.Errorf("ctx is attached with a deadline and it exceeded")
		} else if cerr, ok := err.(*client.ClusterError); ok {
			return fmt.Errorf("*client.ClusterError %v", cerr.Errors())
		} else {
			return fmt.Errorf("bad cluster endpoints, which are not etcd servers: %+v", err)
		}
	}
	return nil
}
Example #28
0
// setCommandFunc executes the "set" command.
func setCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	value, err := argOrStdin(c.Args(), os.Stdin, 1)
	if err != nil {
		handleError(ExitBadArgs, errors.New("value required"))
	}

	ttl := c.Int("ttl")
	prevValue := c.String("swap-with-value")
	prevIndex := c.Int("swap-with-index")

	// TODO: handle transport timeout
	resp, err := ki.Set(context.TODO(), key, value, &client.SetOptions{TTL: time.Duration(ttl) * time.Second, PrevIndex: uint64(prevIndex), PrevValue: prevValue})
	if err != nil {
		handleError(ExitServerError, err)
	}

	printResponseKey(resp, c.GlobalString("output"))
}
Example #29
0
func compareAndSwapUntil(ctx context.Context, tries int, keyid string, kapi etcdc.KeysAPI,
	evaluator func(res *etcdc.Response, setOpts *etcdc.SetOptions) (val string, err error),
) error {
	//uncomment for debugging..
	id := int64(0)
	if Usedtracedlogging {
		id = dice().Int63()
	}
	for i := 0; i < tries; i++ {
		resp, err := kapi.Get(ctx, keyid, &etcdc.GetOptions{Quorum: true})
		if err != nil {
			dlog("%v kapi get error %v", keyid, err)
			return err
		}

		opt := &etcdc.SetOptions{}
		nv, err := evaluator(resp, opt)
		if err != nil {
			dlog("%v eval error %v", keyid, err)
			return err
		}

		dtrace("before: %v \tnewval:%v try:%v idx:%v key:%v", id, nv, i, resp.Index, keyid)
		_, err = kapi.Set(ctx, keyid, nv, opt)
		if err == nil {
			dlog("%v update successful %v", keyid, err)
			return nil
		} else if !IsCompareAndSwapFailure(err) {
			dlog("unexpected error %v", err)
			return err
		}

		dtrace("after : %v \tnewval:%v try:%v key:%v error: %v", id, nv, i, keyid, err)

		backoff(i)
	}

	return CASErrorOutOfRetries
}
Example #30
0
File: etcd.go Project: helgi/pkg
// keysToEtcd copies local keys into etcd.
//
// It only fails if it cannot copy ssh_host_key to sshHostKey. All other
// abnormal conditions are logged, but not considered to be failures.
func keysToEtcd(c cookoo.Context, k client.KeysAPI, ciphers []string, etcdPath string) error {
	lpath := "/etc/ssh/ssh_host_%s_key"
	privkey := "%s/sshHost%sKey"
	for _, cipher := range ciphers {
		path := fmt.Sprintf(lpath, cipher)
		key := fmt.Sprintf(privkey, etcdPath, cipher)
		content, err := ioutil.ReadFile(path)
		if err != nil {
			log.Infof(c, "No key named %s", path)
		} else if _, err := k.Set(dctx(), key, string(content), &client.SetOptions{}); err != nil {
			log.Errf(c, "Could not store ssh key in etcd: %s", err)
		}
	}
	// Now we set the generic key:
	if content, err := ioutil.ReadFile("/etc/ssh/ssh_host_key"); err != nil {
		log.Errf(c, "Could not read the ssh_host_key file.")
		return err
	} else if _, err := k.Set(dctx(), "sshHostKey", string(content), &client.SetOptions{}); err != nil {
		log.Errf(c, "Failed to set sshHostKey in etcd.")
		return err
	}
	return nil
}