Merge pull request #17969 from offlinehacker/pkgs/etcd/update-3.0.6

Update etcd, improve nixos module, fix nixos tests
This commit is contained in:
Jaka Hudoklin 2016-09-04 16:31:50 +02:00 committed by GitHub
commit c083ab99b2
5 changed files with 575 additions and 122 deletions

View file

@ -28,13 +28,13 @@ in {
listenClientUrls = mkOption {
description = "Etcd list of URLs to listen on for client traffic.";
default = ["http://localhost:4001"];
default = ["http://127.0.0.1:2379"];
type = types.listOf types.str;
};
listenPeerUrls = mkOption {
description = "Etcd list of URLs to listen on for peer traffic.";
default = ["http://localhost:7001"];
default = ["http://127.0.0.1:2380"];
type = types.listOf types.str;
};
@ -46,7 +46,7 @@ in {
initialCluster = mkOption {
description = "Etcd initial cluster configuration for bootstrapping.";
default = ["${cfg.name}=http://localhost:7001"];
default = ["${cfg.name}=http://127.0.0.1:2380"];
type = types.listOf types.str;
};
@ -68,6 +68,54 @@ in {
type = types.str;
};
clientCertAuth = mkOption {
description = "Whether to use certs for client authentication";
default = false;
type = types.bool;
};
trustedCaFile = mkOption {
description = "Certificate authority file to use for clients";
default = null;
type = types.nullOr types.path;
};
certFile = mkOption {
description = "Cert file to use for clients";
default = null;
type = types.nullOr types.path;
};
keyFile = mkOption {
description = "Key file to use for clients";
default = null;
type = types.nullOr types.path;
};
peerCertFile = mkOption {
description = "Cert file to use for peer to peer communication";
default = cfg.certFile;
type = types.nullOr types.path;
};
peerKeyFile = mkOption {
description = "Key file to use for peer to peer communication";
default = cfg.keyFile;
type = types.nullOr types.path;
};
peerTrustedCaFile = mkOption {
description = "Certificate authority file to use for peer to peer communication";
default = cfg.trustedCaFile;
type = types.nullOr types.path;
};
peerClientCertAuth = mkOption {
description = "Whether to check all incoming peer requests from the cluster for valid client certificates signed by the supplied CA";
default = false;
type = types.bool;
};
extraConf = mkOption {
description = ''
Etcd extra configuration. See
@ -99,7 +147,7 @@ in {
wantedBy = [ "multi-user.target" ];
after = [ "network-interfaces.target" ];
environment = {
environment = (filterAttrs (n: v: v != null) {
ETCD_NAME = cfg.name;
ETCD_DISCOVERY = cfg.discovery;
ETCD_DATA_DIR = cfg.dataDir;
@ -107,7 +155,14 @@ in {
ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls;
ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls;
ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls;
} // (optionalAttrs (cfg.discovery == ""){
ETCD_PEER_TRUSTED_CA_FILE = cfg.peerTrustedCaFile;
ETCD_PEER_CERT_FILE = cfg.peerCertFile;
ETCD_PEER_KEY_FILE = cfg.peerKeyFile;
ETCD_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth;
ETCD_TRUSTED_CA_FILE = cfg.trustedCaFile;
ETCD_CERT_FILE = cfg.certFile;
ETCD_KEY_FILE = cfg.keyFile;
}) // (optionalAttrs (cfg.discovery == ""){
ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster;
ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState;
ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken;

View file

@ -0,0 +1,157 @@
# This test runs simple etcd cluster
import ./make-test.nix ({ pkgs, ... } : let
runWithOpenSSL = file: cmd: pkgs.runCommand file {
buildInputs = [ pkgs.openssl ];
} cmd;
ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
ca_pem = runWithOpenSSL "ca.pem" ''
openssl req \
-x509 -new -nodes -key ${ca_key} \
-days 10000 -out $out -subj "/CN=etcd-ca"
'';
etcd_key = runWithOpenSSL "etcd-key.pem" "openssl genrsa -out $out 2048";
etcd_csr = runWithOpenSSL "etcd.csr" ''
openssl req \
-new -key ${etcd_key} \
-out $out -subj "/CN=etcd" \
-config ${openssl_cnf}
'';
etcd_cert = runWithOpenSSL "etcd.pem" ''
openssl x509 \
-req -in ${etcd_csr} \
-CA ${ca_pem} -CAkey ${ca_key} \
-CAcreateserial -out $out \
-days 365 -extensions v3_req \
-extfile ${openssl_cnf}
'';
etcd_client_key = runWithOpenSSL "etcd-client-key.pem"
"openssl genrsa -out $out 2048";
etcd_client_csr = runWithOpenSSL "etcd-client-key.pem" ''
openssl req \
-new -key ${etcd_client_key} \
-out $out -subj "/CN=etcd-client" \
-config ${client_openssl_cnf}
'';
etcd_client_cert = runWithOpenSSL "etcd-client.crt" ''
openssl x509 \
-req -in ${etcd_client_csr} \
-CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
-out $out -days 365 -extensions v3_req \
-extfile ${client_openssl_cnf}
'';
openssl_cnf = pkgs.writeText "openssl.cnf" ''
ions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = node1
DNS.2 = node2
DNS.3 = node3
IP.1 = 127.0.0.1
'';
client_openssl_cnf = pkgs.writeText "client-openssl.cnf" ''
ions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
'';
nodeConfig = {
services = {
etcd = {
enable = true;
keyFile = etcd_key;
certFile = etcd_cert;
trustedCaFile = ca_pem;
peerClientCertAuth = true;
listenClientUrls = ["https://127.0.0.1:2379"];
listenPeerUrls = ["https://0.0.0.0:2380"];
};
};
environment.variables = {
ETCDCTL_CERT_FILE = "${etcd_client_cert}";
ETCDCTL_KEY_FILE = "${etcd_client_key}";
ETCDCTL_CA_FILE = "${ca_pem}";
ETCDCTL_PEERS = "https://127.0.0.1:2379";
};
networking.firewall.allowedTCPPorts = [ 2380 ];
};
in {
name = "etcd";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ offline ];
};
nodes = {
node1 = { config, pkgs, nodes, ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
initialAdvertisePeerUrls = ["https://node1:2380"];
};
};
node2 = { config, pkgs, ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
initialAdvertisePeerUrls = ["https://node2:2380"];
};
};
node3 = { config, pkgs, ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380" "node3=https://node3:2380"];
initialAdvertisePeerUrls = ["https://node3:2380"];
initialClusterState = "existing";
};
};
};
testScript = ''
subtest "should start etcd cluster", sub {
$node1->start();
$node2->start();
$node1->waitForUnit("etcd.service");
$node2->waitForUnit("etcd.service");
$node2->waitUntilSucceeds("etcdctl cluster-health");
$node1->succeed("etcdctl set /foo/bar 'Hello world'");
$node2->succeed("etcdctl get /foo/bar | grep 'Hello world'");
};
subtest "should add another member", sub {
$node1->succeed("etcdctl member add node3 https://node3:2380");
$node3->start();
$node3->waitForUnit("etcd.service");
$node3->waitUntilSucceeds("etcdctl member list | grep 'node3'");
$node3->succeed("etcdctl cluster-health");
};
subtest "should survive member crash", sub {
$node3->crash;
$node1->succeed("etcdctl cluster-health");
$node1->succeed("etcdctl set /foo/bar 'Hello degraded world'");
$node1->succeed("etcdctl get /foo/bar | grep 'Hello degraded world'");
};
'';
})

View file

@ -1,111 +1,27 @@
# This test runs etcd as single node, multy node and using discovery
# This test runs simple etcd node
import ./make-test.nix ({ pkgs, ... } : {
name = "etcd";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ offline ];
};
nodes = {
simple =
{ config, pkgs, nodes, ... }:
{
services.etcd.enable = true;
services.etcd.listenClientUrls = ["http://0.0.0.0:4001"];
environment.systemPackages = [ pkgs.curl ];
networking.firewall.allowedTCPPorts = [ 4001 ];
};
node1 =
{ config, pkgs, nodes, ... }:
{
services = {
etcd = {
enable = true;
listenPeerUrls = ["http://0.0.0.0:7001"];
initialAdvertisePeerUrls = ["http://node1:7001"];
initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"];
};
};
networking.firewall.allowedTCPPorts = [ 7001 ];
};
node2 =
{ config, pkgs, ... }:
{
services = {
etcd = {
enable = true;
listenPeerUrls = ["http://0.0.0.0:7001"];
initialAdvertisePeerUrls = ["http://node2:7001"];
initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"];
};
};
networking.firewall.allowedTCPPorts = [ 7001 ];
};
discovery1 =
{ config, pkgs, nodes, ... }:
{
services = {
etcd = {
enable = true;
listenPeerUrls = ["http://0.0.0.0:7001"];
initialAdvertisePeerUrls = ["http://discovery1:7001"];
discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/";
};
};
networking.firewall.allowedTCPPorts = [ 7001 ];
};
discovery2 =
{ config, pkgs, ... }:
{
services = {
etcd = {
enable = true;
listenPeerUrls = ["http://0.0.0.0:7001"];
initialAdvertisePeerUrls = ["http://discovery2:7001"];
discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/";
};
};
networking.firewall.allowedTCPPorts = [ 7001 ];
};
node = { config, pkgs, nodes, ... }: {
services.etcd.enable = true;
};
};
testScript = ''
subtest "single node", sub {
$simple->start();
$simple->waitForUnit("etcd.service");
$simple->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
$simple->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
subtest "should start etcd node", sub {
$node->start();
$node->waitForUnit("etcd.service");
};
subtest "multy node", sub {
$node1->start();
$node2->start();
$node1->waitForUnit("etcd.service");
$node2->waitForUnit("etcd.service");
$node1->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
$node2->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
$node1->shutdown();
$node2->shutdown();
};
subtest "discovery", sub {
$simple->succeed("curl -X PUT http://localhost:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=2");
$discovery1->start();
$discovery2->start();
$discovery1->waitForUnit("etcd.service");
$discovery2->waitForUnit("etcd.service");
$discovery1->waitUntilSucceeds("etcdctl set /foo/bar 'Hello world'");
$discovery2->waitUntilSucceeds("etcdctl get /foo/bar | grep 'Hello world'");
};
subtest "should write and read some values to etcd", sub {
$node->succeed("etcdctl set /foo/bar 'Hello world'");
$node->succeed("etcdctl get /foo/bar | grep 'Hello world'");
}
'';
})

View file

@ -1,20 +1,30 @@
{ stdenv, lib, libpcap, buildGoPackage, fetchFromGitHub }:
with lib;
buildGoPackage rec {
name = "etcd-${version}";
version = "2.3.7";
version = "3.0.6"; # After updating check that nixos tests pass
rev = "v${version}";
goPackagePath = "github.com/coreos/etcd";
src = fetchFromGitHub {
inherit rev;
owner = "coreos";
repo = "etcd";
sha256 = "07rdnhcpnvnkxj5pqacxz669rzn5vw2i1zmf6dd4nv7wpfscdw9f";
sha256 = "163qji360y21nr1wnl16nbvvgdgqgbny4c3v3igp87q9p78sdf75";
};
goDeps = ./deps.json;
buildInputs = [ libpcap ];
meta = {
description = "Distributed reliable key-value store for the most critical data of a distributed system";
license = licenses.asl20;
homepage = https://coreos.com/etcd/;
maintainers = with maintainers; [offline];
platforms = with platforms; linux;
};
}

View file

@ -1,20 +1,335 @@
[
{
"goPackagePath": "github.com/olekukonko/tablewriter",
"fetch": {
"type": "git",
"url": "https://github.com/olekukonko/tablewriter",
"rev": "cca8bbc0798408af109aaaa239cbd2634846b340",
"sha256": "0f9ph3z7lh6p6gihbl1461j9yq5qiaqxr9mzdkp512n18v89ml48"
}
},
{
"goPackagePath": "github.com/mattn/go-runewidth",
"fetch": {
"type": "git",
"url": "https://github.com/mattn/go-runewidth",
"rev": "d6bea18f789704b5f83375793155289da36a3c7f",
"sha256": "1hnigpn7rjbwd1ircxkyx9hvi0xmxr32b2jdy2jzw6b3jmcnz1fs"
}
{
"goPackagePath": "github.com/beorn7/perks",
"fetch": {
"type": "git",
"url": "https://github.com/beorn7/perks",
"rev": "4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9",
"sha256": "1hrybsql68xw57brzj805xx2mghydpdiysv3gbhr7f5wlxj2514y"
}
]
},
{
"goPackagePath": "github.com/boltdb/bolt",
"fetch": {
"type": "git",
"url": "https://github.com/boltdb/bolt",
"rev": "583e8937c61f1af6513608ccc75c97b6abdf4ff9",
"sha256": "0cp5v9iypg9ysiq40k3h3lg7aisxplnmxshha7nama6b170izyay"
}
},
{
"goPackagePath": "github.com/cloudfoundry-incubator/candiedyaml",
"fetch": {
"type": "git",
"url": "https://github.com/cloudfoundry-incubator/candiedyaml",
"rev": "99c3df83b51532e3615f851d8c2dbb638f5313bf",
"sha256": "106nibg7423642gbkg88c5x2jxfz6nmxbribhwb8cr1rn9vpjaxs"
}
},
{
"goPackagePath": "github.com/cockroachdb/cmux",
"fetch": {
"type": "git",
"url": "https://github.com/cockroachdb/cmux",
"rev": "b64f5908f4945f4b11ed4a0a9d3cc1e23350866d",
"sha256": "1by4f3x7j3r3z1sdx1v04r494hn6jaag7lc03prrgx455j8i0jlh"
}
},
{
"goPackagePath": "github.com/coreos/etcd",
"fetch": {
"type": "git",
"url": "https://github.com/coreos/etcd.git",
"rev": "9efa00d1030d4bf62eb8e5ec130023aeb1b8e2d0",
"sha256": "163qji360y21nr1wnl16nbvvgdgqgbny4c3v3igp87q9p78sdf75"
}
},
{
"goPackagePath": "github.com/coreos/go-semver",
"fetch": {
"type": "git",
"url": "https://github.com/coreos/go-semver",
"rev": "8ab6407b697782a06568d4b7f1db25550ec2e4c6",
"sha256": "1gghi5bnqj50hfxhqc1cxmynqmh2yk9ii7ab9gsm75y5cp94ymk0"
}
},
{
"goPackagePath": "github.com/coreos/go-systemd",
"fetch": {
"type": "git",
"url": "https://github.com/coreos/go-systemd",
"rev": "5c49e4850c879a0ddc061e8f4adcf307de8a8bc2",
"sha256": "1w16bnrgfjb5rwha7g8rdjhpgjf8bzmlzhrda5bfvc9ymj3qjibk"
}
},
{
"goPackagePath": "github.com/coreos/pkg",
"fetch": {
"type": "git",
"url": "https://github.com/coreos/pkg",
"rev": "3ac0863d7acf3bc44daf49afef8919af12f704ef",
"sha256": "0l5ans1ls2gknkrnhymgc0zbgg5nqjbjbqc51r611adcr0m6gg8l"
}
},
{
"goPackagePath": "github.com/ghodss/yaml",
"fetch": {
"type": "git",
"url": "https://github.com/ghodss/yaml",
"rev": "aa0c862057666179de291b67d9f093d12b5a8473",
"sha256": "0cbc78n8l7h1gdzhrvahplcvr4v7n8v23vkgskfp843rcx5h6isr"
}
},
{
"goPackagePath": "github.com/gogo/protobuf",
"fetch": {
"type": "git",
"url": "https://github.com/gogo/protobuf",
"rev": "f20a1444730c7d9949b880a0309e737d007def25",
"sha256": "12wa3r2cb2v1m65phbkh692ldlklk459z4x6avpc6im0zkr6r73c"
}
},
{
"goPackagePath": "github.com/golang/protobuf",
"fetch": {
"type": "git",
"url": "https://github.com/golang/protobuf",
"rev": "f592bd283e9ef86337a432eb50e592278c3d534d",
"sha256": "01gxhzn9m6jz6ihwxfycnx39zf5pmkan61l278cnynsb8mibdpvb"
}
},
{
"goPackagePath": "github.com/google/btree",
"fetch": {
"type": "git",
"url": "https://github.com/google/btree",
"rev": "7d79101e329e5a3adf994758c578dab82b90c017",
"sha256": "1c1hsy5s2pfawg3l9954jmqmy4yc2zp3f7i87m00km2yqgb8xpd0"
}
},
{
"goPackagePath": "github.com/grpc-ecosystem/grpc-gateway",
"fetch": {
"type": "git",
"url": "https://github.com/grpc-ecosystem/grpc-gateway",
"rev": "5e0e028ba0a015710eaebf6e47af18812c9f2767",
"sha256": "00s4wxzs6lz5al7y2hxi6r4bxhx5b0ajk5rwxrnb4a4mhlaii8pk"
}
},
{
"goPackagePath": "github.com/jonboulle/clockwork",
"fetch": {
"type": "git",
"url": "https://github.com/jonboulle/clockwork",
"rev": "e3653ace2d63753697e0e5b07b9393971c0bba9d",
"sha256": "1avzqhks12a8x2yzpvjsf3k0gv9cy7zx2z88hn0scacnxkphisvc"
}
},
{
"goPackagePath": "github.com/matttproud/golang_protobuf_extensions",
"fetch": {
"type": "git",
"url": "https://github.com/matttproud/golang_protobuf_extensions",
"rev": "c12348ce28de40eed0136aa2b644d0ee0650e56c",
"sha256": "1d0c1isd2lk9pnfq2nk0aih356j30k3h1gi2w0ixsivi5csl7jya"
}
},
{
"goPackagePath": "github.com/prometheus/client_golang",
"fetch": {
"type": "git",
"url": "https://github.com/prometheus/client_golang",
"rev": "c5b7fccd204277076155f10851dad72b76a49317",
"sha256": "1xqny3147g12n4j03kxm8s9mvdbs3ln6i56c655mybrn9jjy48kd"
}
},
{
"goPackagePath": "github.com/prometheus/client_model",
"fetch": {
"type": "git",
"url": "https://github.com/prometheus/client_model",
"rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6",
"sha256": "11a7v1fjzhhwsl128znjcf5v7v6129xjgkdpym2lial4lac1dhm9"
}
},
{
"goPackagePath": "github.com/prometheus/common",
"fetch": {
"type": "git",
"url": "https://github.com/prometheus/common",
"rev": "ebdfc6da46522d58825777cf1f90490a5b1ef1d8",
"sha256": "0js62pj8600773wx6labpd772yyhz5ivim7dnl7b862wblbmc8mq"
}
},
{
"goPackagePath": "github.com/prometheus/procfs",
"fetch": {
"type": "git",
"url": "https://github.com/prometheus/procfs",
"rev": "abf152e5f3e97f2fafac028d2cc06c1feb87ffa5",
"sha256": "0cp8lznv1b4zhi3wnbjkfxwzhkqd3wbmiy6mwgjanip8l9l3ykws"
}
},
{
"goPackagePath": "github.com/spf13/cobra",
"fetch": {
"type": "git",
"url": "https://github.com/spf13/cobra",
"rev": "7c674d9e72017ed25f6d2b5e497a1368086b6a6f",
"sha256": "0an935r7lc11a744mvdrsy56rs2w0ah3gdclvr4gzd5iqr9ap3dr"
}
},
{
"goPackagePath": "github.com/spf13/pflag",
"fetch": {
"type": "git",
"url": "https://github.com/spf13/pflag",
"rev": "6454a84b6da0ea8b628d5d8a26759f62c6c161b4",
"sha256": "06rfi73jhkncn8gxy6klgmba5947k9gpwdswipdpz680yxczcwna"
}
},
{
"goPackagePath": "github.com/ugorji/go",
"fetch": {
"type": "git",
"url": "https://github.com/ugorji/go",
"rev": "4a1cb5252a6951f715a85d0e4be334c2a2dbf2a2",
"sha256": "0izpijk3piihl4fnqg8ncnp5ivbq41pg3xf7iagg4fbg5id4pxbx"
}
},
{
"goPackagePath": "github.com/xiang90/probing",
"fetch": {
"type": "git",
"url": "https://github.com/xiang90/probing",
"rev": "07dd2e8dfe18522e9c447ba95f2fe95262f63bb2",
"sha256": "0r8rq27yigz72mk8z7p61yjfan8id021dnp1v421ln9byzpvabn2"
}
},
{
"goPackagePath": "golang.org/x/crypto",
"fetch": {
"type": "git",
"url": "https://go.googlesource.com/crypto",
"rev": "88d0005bf4c3ec17306ecaca4281a8d8efd73e91",
"sha256": "1d3x0rwfd4cml06ka8gy74wxrw94m2z7qgz6ky0rgmxcr7p5iikz"
}
},
{
"goPackagePath": "golang.org/x/net",
"fetch": {
"type": "git",
"url": "https://go.googlesource.com/net",
"rev": "7394c112eae4dba7e96bfcfe738e6373d61772b4",
"sha256": "1p8wsxnbsp2lq6hbza2n0zgv4sgpxzzjjlrmcngkhxj47kp3hin7"
}
},
{
"goPackagePath": "google.golang.org/grpc",
"fetch": {
"type": "git",
"url": "https://github.com/grpc/grpc-go",
"rev": "0032a855ba5c8a3c8e0d71c2deef354b70af1584",
"sha256": "0qkynp65jwk6jk932k7kwxs5v6fzlfsb1fay71a00dwr36f44s67"
}
},
{
"goPackagePath": "github.com/urfave/cli",
"fetch": {
"type": "git",
"url": "https://github.com/urfave/cli",
"rev": "168c95418e66e019fe17b8f4f5c45aa62ff80e23",
"sha256": "1gdvvim2f1zigcmbpcgypgn7nvpnlr87grbg7lw13fbpy6fnlw2n"
}
},
{
"goPackagePath": "github.com/mattn/go-runewidth",
"fetch": {
"type": "git",
"url": "https://github.com/mattn/go-runewidth",
"rev": "d6bea18f789704b5f83375793155289da36a3c7f",
"sha256": "1hnigpn7rjbwd1ircxkyx9hvi0xmxr32b2jdy2jzw6b3jmcnz1fs"
}
},
{
"goPackagePath": "github.com/olekukonko/tablewriter",
"fetch": {
"type": "git",
"url": "https://github.com/olekukonko/tablewriter",
"rev": "daf2955e742cf123959884fdff4685aa79b63135",
"sha256": "1fvl251ms7qmzfbi853kdgghqkrmyy6n1605mfy50nhgvw03z203"
}
},
{
"goPackagePath": "github.com/dustin/go-humanize",
"fetch": {
"type": "git",
"url": "https://github.com/dustin/go-humanize",
"rev": "2fcb5204cdc65b4bec9fd0a87606bb0d0e3c54e8",
"sha256": "1m2qgn5vh5m66ggmclgikvwc05np2r7sxgpvlj2jip5d61x29j5k"
}
},
{
"goPackagePath": "github.com/bgentry/speakeasy",
"fetch": {
"type": "git",
"url": "https://github.com/bgentry/speakeasy",
"rev": "a1ccbf2c40dfc8ce514b5c5c6e6d1429ea6880da",
"sha256": "0xqpc1qhdcs5blp1mkrppfb1x0rcv4a445mj0yzdwshbzkw5di01"
}
},
{
"goPackagePath": "github.com/kr/pty",
"fetch": {
"type": "git",
"url": "https://github.com/kr/pty",
"rev": "ce7fa45920dc37a92de8377972e52bc55ffa8d57",
"sha256": "0mdlr2mmwjznw2id0l4200xjajq9dh1kxn3z7d3ksn0b5fwinzmk"
}
},
{
"goPackagePath": "github.com/golang/groupcache",
"fetch": {
"type": "git",
"url": "https://github.com/golang/groupcache",
"rev": "a6b377e3400b08991b80d6805d627f347f983866",
"sha256": "125a6zdaxj916yp2rlrkg8xw00vjf5ga9xwdg4clby8wj4fysma2"
}
},
{
"goPackagePath": "gopkg.in/cheggaaa/pb.v1",
"fetch": {
"type": "git",
"url": "https://gopkg.in/cheggaaa/pb.v1",
"rev": "9453b2db37f4d8bc63751daca63bbe7049eb5e74",
"sha256": "0py7dxvm3ydxcw260x7r7xbjww1vkil3rhyy3f9njmjydyb303rb"
}
},
{
"goPackagePath": "github.com/golang/glog",
"fetch": {
"type": "git",
"url": "https://github.com/golang/glog",
"rev": "23def4e6c14b4da8ac2ed8007337bc5eb5007998",
"sha256": "0jb2834rw5sykfr937fxi8hxi2zy80sj2bdn9b3jb4b26ksqng30"
}
},
{
"goPackagePath": "github.com/spacejam/loghisto",
"fetch": {
"type": "git",
"url": "https://github.com/spacejam/loghisto",
"rev": "9d1d8c1fd2a4ac852bf2e312f2379f553345fda7",
"sha256": "0r31y4ci35pp11wqdyarimdq5a703byk3cf6d67adsa4nw0ysfm1"
}
},
{
"goPackagePath": "github.com/akrennmair/gopcap",
"fetch": {
"type": "git",
"url": "https://github.com/akrennmair/gopcap",
"rev": "00e11033259acb75598ba416495bb708d864a010",
"sha256": "0xfw7x5a36w0g76imjvgk055360xg0nva42qhmflfvll7ldxq96a"
}
}
]