etcd: github.com/coreos/etcd/etcdserver Index | Files | Directories

package etcdserver

import "github.com/coreos/etcd/etcdserver"

Package etcdserver defines how etcd servers interact and store their states.

Index

Package Files

apply.go apply_auth.go apply_v2.go backend.go cluster_util.go config.go consistent_index.go doc.go errors.go metrics.go quota.go raft.go server.go snapshot_merge.go storage.go util.go v2_server.go v3_server.go

Constants

const (
    // DefaultQuotaBytes is the number of bytes the backend Size may
    // consume before exceeding the space quota.
    DefaultQuotaBytes = int64(2 * 1024 * 1024 * 1024) // 2GB
    // MaxQuotaBytes is the maximum number of bytes suggested for a backend
    // quota. A larger quota may lead to degraded performance.
    MaxQuotaBytes = int64(8 * 1024 * 1024 * 1024) // 8GB
)
const (
    DefaultSnapCount = 100000

    StoreClusterPrefix = "/0"
    StoreKeysPrefix    = "/1"

    // HealthInterval is the minimum time the cluster should be healthy
    // before accepting add member requests.
    HealthInterval = 5 * time.Second
)

Variables

var (
    ErrUnknownMethod              = errors.New("etcdserver: unknown method")
    ErrStopped                    = errors.New("etcdserver: server stopped")
    ErrCanceled                   = errors.New("etcdserver: request cancelled")
    ErrTimeout                    = errors.New("etcdserver: request timed out")
    ErrTimeoutDueToLeaderFail     = errors.New("etcdserver: request timed out, possibly due to previous leader failure")
    ErrTimeoutDueToConnectionLost = errors.New("etcdserver: request timed out, possibly due to connection lost")
    ErrTimeoutLeaderTransfer      = errors.New("etcdserver: request timed out, leader transfer took too long")
    ErrNotEnoughStartedMembers    = errors.New("etcdserver: re-configuration failed due to not enough started members")
    ErrNoLeader                   = errors.New("etcdserver: no leader")
    ErrRequestTooLarge            = errors.New("etcdserver: request is too large")
    ErrNoSpace                    = errors.New("etcdserver: no space")
    ErrTooManyRequests            = errors.New("etcdserver: too many requests")
    ErrUnhealthy                  = errors.New("etcdserver: unhealthy cluster")
    ErrKeyNotFound                = errors.New("etcdserver: key not found")
)

func GetClusterFromRemotePeers Uses

func GetClusterFromRemotePeers(urls []string, rt http.RoundTripper) (*membership.RaftCluster, error)

GetClusterFromRemotePeers takes a set of URLs representing etcd peers, and attempts to construct a Cluster by accessing the members endpoint on one of these URLs. The first URL to provide a response is used. If no URLs provide a response, or a Cluster cannot be successfully created from a received response, an error is returned. Each request has a 10-second timeout. Because the upper limit of TTL is 5s, 10 second is enough for building connection and finishing request.

type ApplierV2 Uses

type ApplierV2 interface {
    Delete(r *pb.Request) Response
    Post(r *pb.Request) Response
    Put(r *pb.Request) Response
    QGet(r *pb.Request) Response
    Sync(r *pb.Request) Response
}

ApplierV2 is the interface for processing V2 raft messages

func NewApplierV2 Uses

func NewApplierV2(s store.Store, c *membership.RaftCluster) ApplierV2

type Authenticator Uses

type Authenticator interface {
    AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*pb.AuthEnableResponse, error)
    AuthDisable(ctx context.Context, r *pb.AuthDisableRequest) (*pb.AuthDisableResponse, error)
    Authenticate(ctx context.Context, r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error)
    UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
    UserDelete(ctx context.Context, r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
    UserChangePassword(ctx context.Context, r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
    UserGrantRole(ctx context.Context, r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)
    UserGet(ctx context.Context, r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)
    UserRevokeRole(ctx context.Context, r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)
    RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
    RoleGrantPermission(ctx context.Context, r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)
    RoleGet(ctx context.Context, r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)
    RoleRevokePermission(ctx context.Context, r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)
    RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)
    UserList(ctx context.Context, r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)
    RoleList(ctx context.Context, r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)
}

type DiscoveryError Uses

type DiscoveryError struct {
    Op  string
    Err error
}

func (DiscoveryError) Error Uses

func (e DiscoveryError) Error() string

type EtcdServer Uses

type EtcdServer struct {
    Cfg *ServerConfig

    SyncTicker *time.Ticker
    // contains filtered or unexported fields
}

EtcdServer is the production implementation of the Server interface

func NewServer Uses

func NewServer(cfg *ServerConfig) (srv *EtcdServer, err error)

NewServer creates a new EtcdServer from the supplied configuration. The configuration is considered static for the lifetime of the EtcdServer.

func (*EtcdServer) AddMember Uses

func (s *EtcdServer) AddMember(ctx context.Context, memb membership.Member) ([]*membership.Member, error)

func (*EtcdServer) Alarm Uses

func (s *EtcdServer) Alarm(ctx context.Context, r *pb.AlarmRequest) (*pb.AlarmResponse, error)

func (*EtcdServer) ApplyWait Uses

func (s *EtcdServer) ApplyWait() <-chan struct{}

func (*EtcdServer) AuthDisable Uses

func (s *EtcdServer) AuthDisable(ctx context.Context, r *pb.AuthDisableRequest) (*pb.AuthDisableResponse, error)

func (*EtcdServer) AuthEnable Uses

func (s *EtcdServer) AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*pb.AuthEnableResponse, error)

func (*EtcdServer) AuthInfoFromCtx Uses

func (s *EtcdServer) AuthInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error)

func (*EtcdServer) AuthStore Uses

func (s *EtcdServer) AuthStore() auth.AuthStore

func (*EtcdServer) Authenticate Uses

func (s *EtcdServer) Authenticate(ctx context.Context, r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error)

func (*EtcdServer) Backend Uses

func (s *EtcdServer) Backend() backend.Backend

func (*EtcdServer) Cluster Uses

func (s *EtcdServer) Cluster() *membership.RaftCluster

func (*EtcdServer) ClusterVersion Uses

func (s *EtcdServer) ClusterVersion() *semver.Version

func (*EtcdServer) Compact Uses

func (s *EtcdServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)

func (*EtcdServer) CutPeer Uses

func (s *EtcdServer) CutPeer(id types.ID)

CutPeer drops messages to the specified peer.

func (*EtcdServer) DeleteRange Uses

func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)

func (*EtcdServer) Do Uses

func (s *EtcdServer) Do(ctx context.Context, r pb.Request) (Response, error)

Do interprets r and performs an operation on s.store according to r.Method and other fields. If r.Method is "POST", "PUT", "DELETE", or a "GET" with Quorum == true, r will be sent through consensus before performing its respective operation. Do will block until an action is performed or there is an error.

func (*EtcdServer) HardStop Uses

func (s *EtcdServer) HardStop()

HardStop stops the server without coordination with other members in the cluster.

func (*EtcdServer) ID Uses

func (s *EtcdServer) ID() types.ID

func (*EtcdServer) Index Uses

func (s *EtcdServer) Index() uint64

func (*EtcdServer) IsIDRemoved Uses

func (s *EtcdServer) IsIDRemoved(id uint64) bool

func (*EtcdServer) KV Uses

func (s *EtcdServer) KV() mvcc.ConsistentWatchableKV

func (*EtcdServer) Lead Uses

func (s *EtcdServer) Lead() uint64

Lead is only for testing purposes. TODO: add Raft server interface to expose raft related info: Index, Term, Lead, Committed, Applied, LastIndex, etc.

func (*EtcdServer) Leader Uses

func (s *EtcdServer) Leader() types.ID

func (*EtcdServer) LeaderStats Uses

func (s *EtcdServer) LeaderStats() []byte

func (*EtcdServer) LeaseGrant Uses

func (s *EtcdServer) LeaseGrant(ctx context.Context, r *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error)

func (*EtcdServer) LeaseRenew Uses

func (s *EtcdServer) LeaseRenew(ctx context.Context, id lease.LeaseID) (int64, error)

func (*EtcdServer) LeaseRevoke Uses

func (s *EtcdServer) LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error)

func (*EtcdServer) LeaseTimeToLive Uses

func (s *EtcdServer) LeaseTimeToLive(ctx context.Context, r *pb.LeaseTimeToLiveRequest) (*pb.LeaseTimeToLiveResponse, error)

func (*EtcdServer) Lessor Uses

func (s *EtcdServer) Lessor() lease.Lessor

func (*EtcdServer) MendPeer Uses

func (s *EtcdServer) MendPeer(id types.ID)

MendPeer recovers the message dropping behavior of the given peer.

func (*EtcdServer) PauseSending Uses

func (s *EtcdServer) PauseSending()

func (*EtcdServer) Process Uses

func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error

func (*EtcdServer) Put Uses

func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error)

func (*EtcdServer) RaftHandler Uses

func (s *EtcdServer) RaftHandler() http.Handler

func (*EtcdServer) Range Uses

func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error)

func (*EtcdServer) ReadyNotify Uses

func (s *EtcdServer) ReadyNotify() <-chan struct{}

ReadyNotify returns a channel that will be closed when the server is ready to serve client requests

func (*EtcdServer) RemoveMember Uses

func (s *EtcdServer) RemoveMember(ctx context.Context, id uint64) ([]*membership.Member, error)

func (*EtcdServer) ReportSnapshot Uses

func (s *EtcdServer) ReportSnapshot(id uint64, status raft.SnapshotStatus)

ReportSnapshot reports snapshot sent status to the raft state machine, and clears the used snapshot from the snapshot store.

func (*EtcdServer) ReportUnreachable Uses

func (s *EtcdServer) ReportUnreachable(id uint64)

func (*EtcdServer) ResumeSending Uses

func (s *EtcdServer) ResumeSending()

func (*EtcdServer) RoleAdd Uses

func (s *EtcdServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)

func (*EtcdServer) RoleDelete Uses

func (s *EtcdServer) RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)

func (*EtcdServer) RoleGet Uses

func (s *EtcdServer) RoleGet(ctx context.Context, r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)

func (*EtcdServer) RoleGrantPermission Uses

func (s *EtcdServer) RoleGrantPermission(ctx context.Context, r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)

func (*EtcdServer) RoleList Uses

func (s *EtcdServer) RoleList(ctx context.Context, r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)

func (*EtcdServer) RoleRevokePermission Uses

func (s *EtcdServer) RoleRevokePermission(ctx context.Context, r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)

func (*EtcdServer) SelfStats Uses

func (s *EtcdServer) SelfStats() []byte

func (*EtcdServer) Start Uses

func (s *EtcdServer) Start()

Start prepares and starts server in a new goroutine. It is no longer safe to modify a server's fields after it has been sent to Start. It also starts a goroutine to publish its server information.

func (*EtcdServer) Stop Uses

func (s *EtcdServer) Stop()

Stop stops the server gracefully, and shuts down the running goroutine. Stop should be called after a Start(s), otherwise it will block forever. When stopping leader, Stop transfers its leadership to one of its peers before stopping the server.

func (*EtcdServer) StopNotify Uses

func (s *EtcdServer) StopNotify() <-chan struct{}

StopNotify returns a channel that receives a empty struct when the server is stopped.

func (*EtcdServer) StoreStats Uses

func (s *EtcdServer) StoreStats() []byte

func (*EtcdServer) Term Uses

func (s *EtcdServer) Term() uint64

func (*EtcdServer) TransferLeadership Uses

func (s *EtcdServer) TransferLeadership() error

TransferLeadership transfers the leader to the chosen transferee.

func (*EtcdServer) Txn Uses

func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error)

func (*EtcdServer) UpdateMember Uses

func (s *EtcdServer) UpdateMember(ctx context.Context, memb membership.Member) ([]*membership.Member, error)

func (*EtcdServer) UserAdd Uses

func (s *EtcdServer) UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)

func (*EtcdServer) UserChangePassword Uses

func (s *EtcdServer) UserChangePassword(ctx context.Context, r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)

func (*EtcdServer) UserDelete Uses

func (s *EtcdServer) UserDelete(ctx context.Context, r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)

func (*EtcdServer) UserGet Uses

func (s *EtcdServer) UserGet(ctx context.Context, r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)

func (*EtcdServer) UserGrantRole Uses

func (s *EtcdServer) UserGrantRole(ctx context.Context, r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)

func (*EtcdServer) UserList Uses

func (s *EtcdServer) UserList(ctx context.Context, r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)

func (*EtcdServer) UserRevokeRole Uses

func (s *EtcdServer) UserRevokeRole(ctx context.Context, r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)

func (*EtcdServer) Watchable Uses

func (s *EtcdServer) Watchable() mvcc.WatchableKV

Watchable returns a watchable interface attached to the etcdserver.

type Lessor Uses

type Lessor interface {
    // LeaseGrant sends LeaseGrant request to raft and apply it after committed.
    LeaseGrant(ctx context.Context, r *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error)
    // LeaseRevoke sends LeaseRevoke request to raft and apply it after committed.
    LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error)

    // LeaseRenew renews the lease with given ID. The renewed TTL is returned. Or an error
    // is returned.
    LeaseRenew(ctx context.Context, id lease.LeaseID) (int64, error)

    // LeaseTimeToLive retrieves lease information.
    LeaseTimeToLive(ctx context.Context, r *pb.LeaseTimeToLiveRequest) (*pb.LeaseTimeToLiveResponse, error)
}

type Quota Uses

type Quota interface {
    // Available judges whether the given request fits within the quota.
    Available(req interface{}) bool
    // Cost computes the charge against the quota for a given request.
    Cost(req interface{}) int
    // Remaining is the amount of charge left for the quota.
    Remaining() int64
}

Quota represents an arbitrary quota against arbitrary requests. Each request costs some charge; if there is not enough remaining charge, then there are too few resources available within the quota to apply the request.

func NewBackendQuota Uses

func NewBackendQuota(s *EtcdServer) Quota

type RaftKV Uses

type RaftKV interface {
    Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error)
    Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error)
    DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)
    Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error)
    Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)
}

type RaftTimer Uses

type RaftTimer interface {
    Index() uint64
    Term() uint64
}

type Response Uses

type Response struct {
    Event   *store.Event
    Watcher store.Watcher
    // contains filtered or unexported fields
}

type Server Uses

type Server interface {
    // Start performs any initialization of the Server necessary for it to
    // begin serving requests. It must be called before Do or Process.
    // Start must be non-blocking; any long-running server functionality
    // should be implemented in goroutines.
    Start()
    // Stop terminates the Server and performs any necessary finalization.
    // Do and Process cannot be called after Stop has been invoked.
    Stop()
    // ID returns the ID of the Server.
    ID() types.ID
    // Leader returns the ID of the leader Server.
    Leader() types.ID
    // Do takes a request and attempts to fulfill it, returning a Response.
    Do(ctx context.Context, r pb.Request) (Response, error)
    // Process takes a raft message and applies it to the server's raft state
    // machine, respecting any timeout of the given context.
    Process(ctx context.Context, m raftpb.Message) error
    // AddMember attempts to add a member into the cluster. It will return
    // ErrIDRemoved if member ID is removed from the cluster, or return
    // ErrIDExists if member ID exists in the cluster.
    AddMember(ctx context.Context, memb membership.Member) ([]*membership.Member, error)
    // RemoveMember attempts to remove a member from the cluster. It will
    // return ErrIDRemoved if member ID is removed from the cluster, or return
    // ErrIDNotFound if member ID is not in the cluster.
    RemoveMember(ctx context.Context, id uint64) ([]*membership.Member, error)

    // UpdateMember attempts to update an existing member in the cluster. It will
    // return ErrIDNotFound if the member ID does not exist.
    UpdateMember(ctx context.Context, updateMemb membership.Member) ([]*membership.Member, error)

    // ClusterVersion is the cluster-wide minimum major.minor version.
    // Cluster version is set to the min version that an etcd member is
    // compatible with when first bootstrap.
    //
    // ClusterVersion is nil until the cluster is bootstrapped (has a quorum).
    //
    // During a rolling upgrades, the ClusterVersion will be updated
    // automatically after a sync. (5 second by default)
    //
    // The API/raft component can utilize ClusterVersion to determine if
    // it can accept a client request or a raft RPC.
    // NOTE: ClusterVersion might be nil when etcd 2.1 works with etcd 2.0 and
    // the leader is etcd 2.0. etcd 2.0 leader will not update clusterVersion since
    // this feature is introduced post 2.0.
    ClusterVersion() *semver.Version
}

type ServerConfig Uses

type ServerConfig struct {
    Name           string
    DiscoveryURL   string
    DiscoveryProxy string
    ClientURLs     types.URLs
    PeerURLs       types.URLs
    DataDir        string
    // DedicatedWALDir config will make the etcd to write the WAL to the WALDir
    // rather than the dataDir/member/wal.
    DedicatedWALDir     string
    SnapCount           uint64
    MaxSnapFiles        uint
    MaxWALFiles         uint
    InitialPeerURLsMap  types.URLsMap
    InitialClusterToken string
    NewCluster          bool
    ForceNewCluster     bool
    PeerTLSInfo         transport.TLSInfo

    TickMs           uint
    ElectionTicks    int
    BootstrapTimeout time.Duration

    AutoCompactionRetention int
    QuotaBackendBytes       int64
    MaxTxnOps               uint

    // MaxRequestBytes is the maximum request size to send over raft.
    MaxRequestBytes uint

    StrictReconfigCheck bool

    // ClientCertAuthEnabled is true when cert has been signed by the client CA.
    ClientCertAuthEnabled bool

    AuthToken string
}

ServerConfig holds the configuration of etcd as taken from the command line or discovery.

func (*ServerConfig) MemberDir Uses

func (c *ServerConfig) MemberDir() string

func (*ServerConfig) Print Uses

func (c *ServerConfig) Print()

func (*ServerConfig) PrintWithInitial Uses

func (c *ServerConfig) PrintWithInitial()

func (*ServerConfig) ReqTimeout Uses

func (c *ServerConfig) ReqTimeout() time.Duration

ReqTimeout returns timeout for request to finish.

func (*ServerConfig) ShouldDiscover Uses

func (c *ServerConfig) ShouldDiscover() bool

func (*ServerConfig) SnapDir Uses

func (c *ServerConfig) SnapDir() string

func (*ServerConfig) VerifyBootstrap Uses

func (c *ServerConfig) VerifyBootstrap() error

VerifyBootstrap sanity-checks the initial config for bootstrap case and returns an error for things that should never happen.

func (*ServerConfig) VerifyJoinExisting Uses

func (c *ServerConfig) VerifyJoinExisting() error

VerifyJoinExisting sanity-checks the initial config for join existing cluster case and returns an error for things that should never happen.

func (*ServerConfig) WALDir Uses

func (c *ServerConfig) WALDir() string

type Storage Uses

type Storage interface {
    // Save function saves ents and state to the underlying stable storage.
    // Save MUST block until st and ents are on stable storage.
    Save(st raftpb.HardState, ents []raftpb.Entry) error
    // SaveSnap function saves snapshot to the underlying stable storage.
    SaveSnap(snap raftpb.Snapshot) error
    // Close closes the Storage and performs finalization.
    Close() error
}

func NewStorage Uses

func NewStorage(w *wal.WAL, s *snap.Snapshotter) Storage

Directories

PathSynopsis
apiPackage api manages the capabilities and features that are exposed to clients by the etcd cluster.
api/v2httpPackage v2http provides etcd client and server implementations.
api/v2http/httptypesPackage httptypes defines how etcd's HTTP API entities are serialized to and deserialized from JSON.
api/v3clientPackage v3client provides clientv3 interfaces from an etcdserver.
api/v3electionPackage v3election provides a v3 election service from an etcdserver.
api/v3election/v3electionpbPackage v3electionpb is a generated protocol buffer package.
api/v3election/v3electionpb/gwPackage v3electionpb is a reverse proxy.
api/v3lockPackage v3lock provides a v3 locking service from an etcdserver.
api/v3lock/v3lockpbPackage v3lockpb is a generated protocol buffer package.
api/v3lock/v3lockpb/gwPackage v3lockpb is a reverse proxy.
api/v3rpcPackage v3rpc implements etcd v3 RPC system based on gRPC.
api/v3rpc/rpctypesPackage rpctypes has types and values shared by the etcd server and client for v3 RPC interaction.
authPackage auth implements etcd authentication.
etcdserverpbPackage etcdserverpb is a generated protocol buffer package.
etcdserverpb/gwPackage etcdserverpb is a reverse proxy.
membershipPackage membership describes individual etcd members and clusters of members.
statsPackage stats defines a standard interface for etcd cluster statistics.

Package etcdserver imports 57 packages (graph) and is imported by 279 packages. Updated 2017-05-26. Refresh now. Tools for package owners.