diff --git a/Dockerfile b/Dockerfile index a2ee25d4..25d368be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM golang:1.13.1-stretch -MAINTAINER Andrew Hill +FROM golang:1.13.5-buster +MAINTAINER Textile # This is (in large part) copied (with love) from # https://hub.docker.com/r/ipfs/go-ipfs/dockerfile @@ -14,7 +14,7 @@ RUN cd $SRC_DIR \ COPY . $SRC_DIR -# build source +# Install the daemon RUN cd $SRC_DIR \ && go install github.com/textileio/go-threads/threadsd @@ -36,42 +36,41 @@ RUN set -x \ RUN apt-get update && apt-get install -y ca-certificates # Now comes the actual target image, which aims to be as small as possible. -FROM busybox:1-glibc -MAINTAINER Sander Pick +FROM busybox:1.31.0-glibc +LABEL maintainer="Textile " # Get the threads binary, entrypoint script, and TLS CAs from the build container. ENV SRC_DIR /go-threads COPY --from=0 /go/bin/threadsd /usr/local/bin/threadsd -COPY --from=0 $SRC_DIR/bin/container_daemon /usr/local/bin/start_threadsd COPY --from=0 /tmp/su-exec/su-exec /sbin/su-exec COPY --from=0 /tmp/tini /sbin/tini COPY --from=0 /etc/ssl/certs /etc/ssl/certs # This shared lib (part of glibc) doesn't seem to be included with busybox. -# COPY --from=0 /lib/x86_64-linux-gnu/libdl-2.24.so /lib/libdl.so.2 +COPY --from=0 /lib/x86_64-linux-gnu/libdl.so.2 /lib/libdl.so.2 -# Create the fs-repo directory -ENV THREADS_REPO /data/threads -RUN mkdir -p $THREADS_REPO \ - && adduser -D -h $THREADS_REPO -u 1000 -G users textile \ - && chown -R textile:users $THREADS_REPO +# hostAddr; should be exposed to the public +EXPOSE 4006 +# hostProxyAddr; should be exposed to the public +EXPOSE 5006 +# apiAddr; should be exposed to the public +EXPOSE 6006 +# apiProxyAddr; should be exposed to the public +EXPOSE 7006 + +# Create the repo directory and switch to a non-privileged user. +ENV THREADS_PATH /data/threads +RUN mkdir -p $THREADS_PATH \ + && adduser -D -h $THREADS_PATH -u 1000 -G users textile \ + && chown -R textile:users $THREADS_PATH # Switch to a non-privileged user USER textile -# Expose the fs-repo as a volume. -# start_threadsd initializes an fs-repo if none is mounted. +# Expose the repo as a volume. # Important this happens after the USER directive so permission are correct. -VOLUME $THREADS_REPO - -EXPOSE 4006 -EXPOSE 5006 -EXPOSE 6006 -EXPOSE 7006 +VOLUME $THREADS_PATH -# This just makes sure that: -# 1. There's an fs-repo, and initializes one if there isn't. -# 2. The API and Gateway are accessible from outside the container. -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/start_threadsd"] +ENTRYPOINT ["/sbin/tini", "--", "threadsd"] -CMD ["threadsd", "--repo=/data/threads", "--apiAddr=/ip4/0.0.0.0/tcp/6006", "--apiProxyAddr=/ip4/0.0.0.0/tcp/7006"] +CMD ["--repo=/data/threads", "--apiAddr=/ip4/0.0.0.0/tcp/6006", "--apiProxyAddr=/ip4/0.0.0.0/tcp/7006"] diff --git a/api/client/client.go b/api/client/client.go index b4129d94..1cb5da13 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -11,18 +11,11 @@ import ( pb "github.com/textileio/go-threads/api/pb" "github.com/textileio/go-threads/crypto/symmetric" "github.com/textileio/go-threads/store" - "github.com/textileio/go-threads/util" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// Client provides the client api -type Client struct { - client pb.APIClient - conn *grpc.ClientConn -} - // ActionType describes the type of event action when subscribing to data updates type ActionType int @@ -70,21 +63,22 @@ type ListenEvent struct { err error } +// Client provides the client api +type Client struct { + c pb.APIClient + conn *grpc.ClientConn +} + // NewClient starts the client -func NewClient(maddr ma.Multiaddr) (*Client, error) { - addr, err := util.TCPAddrFromMultiAddr(maddr) - if err != nil { - return nil, err - } - conn, err := grpc.Dial(addr, grpc.WithInsecure()) +func NewClient(target string, opts ...grpc.DialOption) (*Client, error) { + conn, err := grpc.Dial(target, opts...) if err != nil { return nil, err } - client := &Client{ - client: pb.NewAPIClient(conn), - conn: conn, - } - return client, nil + return &Client{ + c: pb.NewAPIClient(conn), + conn: conn, + }, nil } // Close closes the client's grpc connection and cancels any active requests @@ -94,7 +88,7 @@ func (c *Client) Close() error { // NewStore cereates a new Store func (c *Client) NewStore(ctx context.Context) (string, error) { - resp, err := c.client.NewStore(ctx, &pb.NewStoreRequest{}) + resp, err := c.c.NewStore(ctx, &pb.NewStoreRequest{}) if err != nil { return "", err } @@ -108,13 +102,13 @@ func (c *Client) RegisterSchema(ctx context.Context, storeID, name, schema strin Name: name, Schema: schema, } - _, err := c.client.RegisterSchema(ctx, req) + _, err := c.c.RegisterSchema(ctx, req) return err } // Start starts the specified Store func (c *Client) Start(ctx context.Context, storeID string) error { - _, err := c.client.Start(ctx, &pb.StartRequest{StoreID: storeID}) + _, err := c.c.Start(ctx, &pb.StartRequest{StoreID: storeID}) return err } @@ -126,7 +120,7 @@ func (c *Client) StartFromAddress(ctx context.Context, storeID string, addr ma.M FollowKey: followKey.Bytes(), ReadKey: readKey.Bytes(), } - _, err := c.client.StartFromAddress(ctx, req) + _, err := c.c.StartFromAddress(ctx, req) return err } @@ -143,7 +137,7 @@ func (c *Client) ModelCreate(ctx context.Context, storeID, modelName string, ite Values: values, } - resp, err := c.client.ModelCreate(ctx, req) + resp, err := c.c.ModelCreate(ctx, req) if err != nil { return err } @@ -170,7 +164,7 @@ func (c *Client) ModelSave(ctx context.Context, storeID, modelName string, items ModelName: modelName, Values: values, } - _, err = c.client.ModelSave(ctx, req) + _, err = c.c.ModelSave(ctx, req) return err } @@ -181,7 +175,7 @@ func (c *Client) ModelDelete(ctx context.Context, storeID, modelName string, ent ModelName: modelName, EntityIDs: entityIDs, } - _, err := c.client.ModelDelete(ctx, req) + _, err := c.c.ModelDelete(ctx, req) return err } @@ -190,7 +184,7 @@ func (c *Client) GetStoreLink(ctx context.Context, storeID string) ([]string, er req := &pb.GetStoreLinkRequest{ StoreID: storeID, } - resp, err := c.client.GetStoreLink(ctx, req) + resp, err := c.c.GetStoreLink(ctx, req) if err != nil { return nil, err } @@ -212,7 +206,7 @@ func (c *Client) ModelHas(ctx context.Context, storeID, modelName string, entity ModelName: modelName, EntityIDs: entityIDs, } - resp, err := c.client.ModelHas(ctx, req) + resp, err := c.c.ModelHas(ctx, req) if err != nil { return false, err } @@ -230,7 +224,7 @@ func (c *Client) ModelFind(ctx context.Context, storeID, modelName string, query ModelName: modelName, QueryJSON: queryBytes, } - resp, err := c.client.ModelFind(ctx, req) + resp, err := c.c.ModelFind(ctx, req) if err != nil { return nil, err } @@ -244,7 +238,7 @@ func (c *Client) ModelFindByID(ctx context.Context, storeID, modelName, entityID ModelName: modelName, EntityID: entityID, } - resp, err := c.client.ModelFindByID(ctx, req) + resp, err := c.c.ModelFindByID(ctx, req) if err != nil { return err } @@ -254,7 +248,7 @@ func (c *Client) ModelFindByID(ctx context.Context, storeID, modelName, entityID // ReadTransaction returns a read transaction that can be started and used and ended func (c *Client) ReadTransaction(ctx context.Context, storeID, modelName string) (*ReadTransaction, error) { - client, err := c.client.ReadTransaction(ctx) + client, err := c.c.ReadTransaction(ctx) if err != nil { return nil, err } @@ -263,7 +257,7 @@ func (c *Client) ReadTransaction(ctx context.Context, storeID, modelName string) // WriteTransaction returns a read transaction that can be started and used and ended func (c *Client) WriteTransaction(ctx context.Context, storeID, modelName string) (*WriteTransaction, error) { - client, err := c.client.WriteTransaction(ctx) + client, err := c.c.WriteTransaction(ctx) if err != nil { return nil, err } @@ -298,7 +292,7 @@ func (c *Client) Listen(ctx context.Context, storeID string, listenOptions ...Li StoreID: storeID, Filters: filters, } - stream, err := c.client.Listen(ctx, req) + stream, err := c.c.Listen(ctx, req) if err != nil { return nil, err } diff --git a/api/client/client_test.go b/api/client/client_test.go index 34bea410..1ade9612 100644 --- a/api/client/client_test.go +++ b/api/client/client_test.go @@ -3,6 +3,7 @@ package client import ( "context" "encoding/json" + "fmt" "io/ioutil" "os" "reflect" @@ -10,9 +11,11 @@ import ( "time" ma "github.com/multiformats/go-multiaddr" + "github.com/phayes/freeport" "github.com/textileio/go-threads/api" "github.com/textileio/go-threads/store" "github.com/textileio/go-threads/util" + "google.golang.org/grpc" ) const modelName = "Person" @@ -47,8 +50,8 @@ const schema = `{ var ( shutdown func() client *Client - clientAddr = "/ip4/127.0.0.1/tcp/9090" - clientProxyAddr = "/ip4/127.0.0.1/tcp/0" + clientAddr string + clientProxyAddr string ) type Person struct { @@ -469,6 +472,15 @@ func makeServer() (*api.Server, func()) { panic(err) } ts.Bootstrap(util.DefaultBoostrapPeers()) + + port, err := freeport.GetFreePort() + if err != nil { + panic(err) + } + + clientAddr = fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port) + clientProxyAddr = "/ip4/127.0.0.1/tcp/0" + apiAddr, err := ma.NewMultiaddr(clientAddr) if err != nil { panic(err) @@ -500,7 +512,11 @@ func makeClient() *Client { if err != nil { panic(err) } - client, err := NewClient(addr) + target, err := util.TCPAddrFromMultiAddr(addr) + if err != nil { + panic(err) + } + client, err := NewClient(target, grpc.WithInsecure()) if err != nil { panic(err) } diff --git a/api/server.go b/api/server.go index 1a08e912..f2db13af 100644 --- a/api/server.go +++ b/api/server.go @@ -40,7 +40,7 @@ type Config struct { // NewServer starts and returns a new server with the given threadservice. // The threadservice is *not* managed by the server. -func NewServer(ctx context.Context, ts core.Service, conf Config) (*Server, error) { +func NewServer(ctx context.Context, ts core.Service, conf Config, opts ...grpc.ServerOption) (*Server, error) { var err error if conf.Debug { err = util.SetLogLevels(map[string]logging.LogLevel{ @@ -62,7 +62,7 @@ func NewServer(ctx context.Context, ts core.Service, conf Config) (*Server, erro ctx, cancel := context.WithCancel(ctx) s := &Server{ - rpc: grpc.NewServer(), + rpc: grpc.NewServer(opts...), service: &service{manager: manager}, ctx: ctx, cancel: cancel, diff --git a/bin/container_daemon b/bin/container_daemon deleted file mode 100755 index b59638af..00000000 --- a/bin/container_daemon +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# this file requires sh over bash: -set -e -user=textile -repo="$THREADS_REPO" - -if [ $(id -u) -eq 0 ]; then - echo "Changing user to $user" - # ensure folder is writable - su-exec "$user" test -w "$repo" || chown -R -- "$user" "$repo" - # restart script with new privileges - exec su-exec "$user" "$0" "$@" -fi - -# if the first argument is daemon -if [ "$1" = "threadsd" ]; then - shift -fi - -exec threadsd "$@" diff --git a/go.mod b/go.mod index aedc5b94..af74a17f 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( github.com/multiformats/go-multihash v0.0.10 github.com/multiformats/go-varint v0.0.2 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 // indirect github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1 // indirect github.com/rs/cors v1.7.0 // indirect github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc diff --git a/go.sum b/go.sum index a0b6ec05..5f39cb16 100644 --- a/go.sum +++ b/go.sum @@ -522,6 +522,8 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=