etcdserver/server.go
func (s *EtcdServer) publish(timeout time.Duration) {
	b, err := json.Marshal(s.attributes)
	if err != nil {
		if lg := s.getLogger(); lg != nil {
			lg.Panic("failed to marshal JSON", zap.Error(err))
		} else {
			plog.Panicf("json marshal error: %v", err)
		}
		return
	}
	req := pb.Request{
		Method: "PUT",
		Path:   membership.MemberAttributesStorePath(s.id),
		Val:    string(b),
	}

	for {
		ctx, cancel := context.WithTimeout(s.ctx, timeout)
		_, err := s.Do(ctx, req)
		cancel()
		switch err {
		case nil:
			close(s.readych)
			if lg := s.getLogger(); lg != nil {
				lg.Info(
					"published local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.String("cluster-id", s.cluster.ID().String()),
					zap.Duration("publish-timeout", timeout),
				)
			} else {
				plog.Infof("published %+v to cluster %s", s.attributes, s.cluster.ID())
			}
			return

		case ErrStopped:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"stopped publish because server is stopped",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Infof("aborting publish because server is stopped")
			}
			return

		default:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"failed to publish local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Errorf("publish error: %v", err)
			}
		}
	}
}
func (s *EtcdServer) publish(timeout time.Duration) {
	b, err := json.Marshal(s.attributes)
	if err != nil {
		if lg := s.getLogger(); lg != nil {
			lg.Panic("failed to marshal JSON", zap.Error(err))
		} else {
			plog.Panicf("json marshal error: %v", err)
		}
		return
	}
	req := pb.Request{
		Method: "PUT",
		Path:   membership.MemberAttributesStorePath(s.id),
		Val:    string(b),
	}

	for {
		ctx, cancel := context.WithTimeout(s.ctx, timeout)
		_, err := s.Do(ctx, req)
		cancel()
		switch err {
		case nil:
			close(s.readych)
			if lg := s.getLogger(); lg != nil {
				lg.Info(
					"published local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.String("cluster-id", s.cluster.ID().String()),
					zap.Duration("publish-timeout", timeout),
				)
			} else {
				plog.Infof("published %+v to cluster %s", s.attributes, s.cluster.ID())
			}
			return

		case ErrStopped:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"stopped publish because server is stopped",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Infof("aborting publish because server is stopped")
			}
			return

		default:
			if lg := s.getLogger(); lg != nil {
				lg.Warn(
					"failed to publish local member to cluster through raft",
					zap.String("local-member-id", s.ID().String()),
					zap.String("local-member-attributes", fmt.Sprintf("%+v", s.attributes)),
					zap.String("request-path", req.Path),
					zap.Duration("publish-timeout", timeout),
					zap.Error(err),
				)
			} else {
				plog.Errorf("publish error: %v", err)
			}
		}
	}
}
// publish registers server information into the cluster. The information
// is the JSON representation of this server's member struct, updated with the
// static clientURLs of the server.
// The function keeps attempting to register until it succeeds,
// or its server is stopped.