From 111b4401d4597c6a97b7a5316bcd35e697ab07ff Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Tue, 23 May 2017 03:14:40 -0700 Subject: [PATCH] Release Tiny Alpine 3.5 Docker Image (#24) * Add new Dockerfile * Test custom build-harness * Test 2-stage build process * undo test * fix path * fix order of operations * Rewrite to use docker for building * remove language declaration * escape $ * Fix vars * Disable test * fix test * golang:1.8 test fixes * golang:1.8 test fixes * add group tpl * Debug logging * missing brace * define log * use gid for test * fix test * fix lint * remove unused dockerfile * add libc6-compat * Export DOCKER_IMAGE_NAME * add ca-certificates --- .travis.yml | 39 ++++++------------------------ Dockerfile | 48 ++++++------------------------------- Makefile | 38 +++++++++++++++++++++++++++++ README.md | 2 +- api/github.go | 39 +++++++++++++++--------------- api/linux.go | 3 +++ api/linux_test.go | 22 ++++++++--------- api/linux_users.go | 4 ++-- key_storages/github_keys.go | 4 ++-- 9 files changed, 90 insertions(+), 109 deletions(-) diff --git a/.travis.yml b/.travis.yml index 958ece3..b312bee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,4 @@ sudo: required -language: go -go: - - 1.7.x -env: - global: - - DOCKER_IMAGE_NAME=cloudposse/github-authorized-keys - - RUN_TESTS=1 - - TEST_GITHUB_API_TOKEN=${GITHUB_API_TOKEN} - - TEST_GITHUB_ORGANIZATION=${GITHUB_ORGANIZATION} - - TEST_GITHUB_TEAM=${GITHUB_TEAM} - - TEST_GITHUB_TEAM_ID=${GITHUB_TEAM_ID} - - TEST_GITHUB_USER=${GITHUB_USER} services: - docker @@ -25,27 +13,16 @@ script: - export TEST_ETCD_ENDPOINT=http://${ETCD_IP}:2379 - - docker-compose -f docker-compose-test.yaml up -d +# setup testing environment for etcd + - make compose-up - - |- - make docker:build \ - ARGS="RUN_TESTS \ - TEST_GITHUB_API_TOKEN \ - TEST_GITHUB_ORGANIZATION \ - TEST_GITHUB_TEAM \ - TEST_GITHUB_TEAM_ID \ - TEST_GITHUB_USER \ - TEST_ETCD_ENDPOINT" - -# Also build packages for release - - make go:deps-dev - - make go:deps-build - - make go:deps - - make go:build-all - - ls -l release/ +# cross-compile & run tests + - make ci -after_success: +# build a slim alpine image that ships only with the release binary - make docker:build + +after_success: - make travis:docker-tag-and-push deploy: @@ -56,5 +33,3 @@ deploy: skip_cleanup: true on: tags: true - - diff --git a/Dockerfile b/Dockerfile index 1d5736d..f595ea9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,44 +1,6 @@ -FROM golang:1.7-alpine +FROM alpine:3.5 -COPY ./ /go/src/github.com/cloudposse/github-authorized-keys - -WORKDIR /go/src/github.com/cloudposse/github-authorized-keys - -ARG RUN_TESTS=0 -ARG TEST_GITHUB_API_TOKEN -ARG TEST_GITHUB_ORGANIZATION -ARG TEST_GITHUB_TEAM -ARG TEST_GITHUB_TEAM_ID -ARG TEST_GITHUB_USER -ARG TEST_ETCD_ENDPOINT - -# We do tests on alpine so use alpine adduser flags - -ENV TEST_LINUX_USER_ADD_TPL "adduser -D -s {shell} {username}" -ENV TEST_LINUX_USER_ADD_WITH_GID_TPL "adduser -D -s {shell} -G {group} {username}" -ENV TEST_LINUX_USER_ADD_TO_GROUP_TPL "adduser {username} {group}" -ENV TEST_LINUX_USER_DEL_TPL "deluser {username}" - -ENV GIN_MODE=release - -RUN set -ex \ - && apk add --no-cache --virtual .build-deps \ - git \ - make \ - curl \ - bash \ - && make init \ - && make go:deps-dev \ - && make go:lint \ - && make go:deps-build \ - && make go:deps \ - && ( [[ $RUN_TESTS -eq 0 ]] || make go:test; ) \ - && make go:build \ - && go-wrapper install \ - && rm -rf /go/src \ - && apk del .build-deps - -WORKDIR $GOPATH +WORKDIR / # For production run most common user add flags # @@ -69,10 +31,14 @@ ENV SYNC_USERS_INTERVAL= ENV INTEGRATE_SSH=false - ENV LISTEN=":301" # For production we run container with host network, so expose is just for testing and CI\CD EXPOSE 301 +RUN apk --update --no-cache add libc6-compat ca-certificates && \ + ln -s /lib /lib64 + +COPY ./release/github-authorized-keys_linux_amd64 /usr/bin/github-authorized-keys + ENTRYPOINT ["github-authorized-keys"] diff --git a/Makefile b/Makefile index affb49d..5fd149f 100644 --- a/Makefile +++ b/Makefile @@ -15,13 +15,51 @@ RELEASE_ARCH += openbsd/386 RELEASE_ARCH += openbsd/amd64 APP := github-authorized-keys + + COPYRIGHT_SOFTWARE := Github Authorized Keys COPYRIGHT_SOFTWARE_DESCRIPTION := Use GitHub teams to manage system user accounts and authorized_keys +export DOCKER_IMAGE_NAME = cloudposse/$(APP) + include $(shell curl -so .build-harness "https://raw.githubusercontent.com/cloudposse/build-harness/master/templates/Makefile.build-harness"; echo .build-harness) +## Execute local deps deps: $(SELF) go:deps go:deps-dev go:deps-build +## Execute local build build: $(SELF) go:build + +## Execute all targets +all: + $(SELF) go:deps-dev + $(SELF) go:deps-build + $(SELF) go:deps + $(SELF) go:lint + $(SELF) go:test + $(SELF) go:build-all + +## Bring up docker compose environment +compose-up: + docker-compose -f docker-compose-test.yaml up -d + +## Entrypoint for CI +ci: + @docker run \ + -e GIN_MODE=release \ + -e RUN_TESTS=1 \ + -e LOG_LEVEL=debug \ + -e TEST_GITHUB_API_TOKEN=$(GITHUB_API_TOKEN) \ + -e TEST_GITHUB_ORGANIZATION=$(GITHUB_ORGANIZATION) \ + -e TEST_GITHUB_TEAM=$(GITHUB_TEAM) \ + -e TEST_GITHUB_TEAM_ID=$(GITHUB_TEAM_ID) \ + -e TEST_GITHUB_USER=$(GITHUB_USER) \ + -e TEST_LINUX_USER_ADD_TPL="adduser --shell {shell} {username}" \ + -e TEST_LINUX_USER_ADD_WITH_GID_TPL="adduser --shell {shell} --gid {gid} {username}" \ + -e TEST_LINUX_USER_ADD_TO_GROUP_TPL="adduser {username} {group}" \ + -e TEST_LINUX_USER_DEL_TPL="deluser {username}" \ + --volume=$$(pwd):/go/src/github.com/cloudposse/github-authorized-keys \ + golang:1.8 make -C /go/src/github.com/cloudposse/github-authorized-keys all + @ls -l release/ diff --git a/README.md b/README.md index 906bdca..4c6aa04 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Below are some of the settings which can be tweaked. | Environment Variable | **Description** | **Default** |--------------------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------- | `LINUX_USER_ADD_TPL` | Command used to add a user to the system when no default group supplied. | `adduser {username} --disabled-password --force-badname --shell {shell}` -| `LINUX_USER_ADD_WITH_GID_TPL` | Command used to add a user to the system when a default primary group supplied. | `adduser {username} --disabled-password --force-badname --shell {shell} --group {group}` +| `LINUX_USER_ADD_WITH_GID_TPL` | Command used to add a user to the system when a default primary gid supplied . | `adduser {username} --disabled-password --force-badname --shell {shell} --gid {gid|group}` | `LINUX_USER_ADD_TO_GROUP_TPL` | Command used to add the user to secondary groups | `adduser {username} {group}` | `LINUX_USER_DEL_TPL` | Command used to delete a user from the system when removed the the team | `deluser {username}` | `SSH_RESTART_TPL` | Command used to restart SSH when `INTEGRATE_SSH=true` | `/usr/sbin/service ssh force-reload` diff --git a/api/github.go b/api/github.go index 2fecc92..05e0238 100644 --- a/api/github.go +++ b/api/github.go @@ -21,10 +21,10 @@ package api import ( "errors" - "github.com/google/go-github/github" - "golang.org/x/oauth2" log "github.com/Sirupsen/logrus" + "github.com/google/go-github/github" "github.com/spf13/viper" + "golang.org/x/oauth2" ) var ( @@ -36,7 +36,6 @@ var ( // ErrorGitHubNotFound - returned when github.com resource not found ErrorGitHubNotFound = errors.New("Not found") - ) func init() { @@ -69,7 +68,7 @@ func (c *GithubClient) GetTeam(name string, id int) (team *github.Team, err erro err = nil var opt = &github.ListOptions{ - PerPage: viper.GetInt("github_api_max_page_size"), + PerPage: viper.GetInt("github_api_max_page_size"), } for { @@ -78,13 +77,13 @@ func (c *GithubClient) GetTeam(name string, id int) (team *github.Team, err erro if response.StatusCode != 200 { err = ErrorGitHubAccessDenied return - } else { - for _, localTeam := range teams { - if *localTeam.ID == id || *localTeam.Slug == name { - team = localTeam - // team found - return - } + } + + for _, localTeam := range teams { + if *localTeam.ID == id || *localTeam.Slug == name { + team = localTeam + // team found + return } } @@ -131,7 +130,7 @@ func (c *GithubClient) GetKeys(userName string) (keys []*github.Key, err error) } for { - items, response, local_err := c.client.Users.ListKeys(userName, opt) + items, response, localErr := c.client.Users.ListKeys(userName, opt) logger.Debugf("Response: %v", response) logger.Debugf("Response.StatusCode: %v", response.StatusCode) @@ -147,8 +146,8 @@ func (c *GithubClient) GetKeys(userName string) (keys []*github.Key, err error) return } - if local_err != nil { - err = local_err + if localErr != nil { + err = localErr return } @@ -165,7 +164,7 @@ func (c *GithubClient) GetKeys(userName string) (keys []*github.Key, err error) func (c *GithubClient) GetTeamMembers(team *github.Team) (users []*github.User, err error) { defer func() { if r := recover(); r != nil { - users = make([]*github.User, 0) + users = make([]*github.User, 0) err = ErrorGitHubConnectionFailed } }() @@ -175,17 +174,17 @@ func (c *GithubClient) GetTeamMembers(team *github.Team) (users []*github.User, PerPage: viper.GetInt("github_api_max_page_size"), }, } - + for { - members, resp, local_err := c.client.Organizations.ListTeamMembers(*team.ID, opt) + members, resp, localErr := c.client.Organizations.ListTeamMembers(*team.ID, opt) if resp.StatusCode != 200 { return nil, ErrorGitHubAccessDenied } - if local_err != nil { - err = local_err + if localErr != nil { + err = localErr return } - + users = append(users, members...) if resp.LastPage == 0 { diff --git a/api/linux.go b/api/linux.go index 04a2afd..9221781 100644 --- a/api/linux.go +++ b/api/linux.go @@ -20,6 +20,7 @@ package api import ( "bytes" + log "github.com/Sirupsen/logrus" "os/exec" "strings" @@ -83,7 +84,9 @@ func (linux *Linux) Command(name string, params ...string) *exec.Cmd { // TemplateCommand - creates command based on template and args with placeholders. func (linux *Linux) TemplateCommand(template string, args map[string]interface{}) *exec.Cmd { + logger := log.WithFields(log.Fields{"class": "Linux", "method": "TemplateCommand"}) t := fasttemplate.New(template, "{", "}") cmd := strings.Split(t.ExecuteString(args), " ") + logger.Debugf("Command: %v", cmd) return linux.Command(cmd[0], cmd[1:]...) } diff --git a/api/linux_test.go b/api/linux_test.go index 0185320..aa79873 100644 --- a/api/linux_test.go +++ b/api/linux_test.go @@ -20,11 +20,11 @@ package api import ( "fmt" + model "github.com/cloudposse/github-authorized-keys/model/linux" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "os/user" "strconv" - model "github.com/cloudposse/github-authorized-keys/model/linux" ) var _ = Describe("Linux", func() { @@ -89,7 +89,7 @@ var _ = Describe("Linux", func() { ) BeforeEach(func() { - userName = model.NewUser("test", "", []string{"wheel", "root"}, "/bin/bash") + userName = model.NewUser("test", "", []string{"operator", "root"}, "/bin/bash") linux = NewLinux("/") }) @@ -184,14 +184,14 @@ var _ = Describe("Linux", func() { Context("call with existing group", func() { It("should return valid group", func() { linux := NewLinux("/") - group, err := linux.groupLookup("wheel") + group, err := linux.groupLookup("operator") Expect(err).To(BeNil()) Expect(group).NotTo(BeNil()) - Expect(group.Gid).To(Equal("10")) - Expect(group.Name).To(Equal("wheel")) + Expect(group.Gid).To(Equal("37")) + Expect(group.Name).To(Equal("operator")) }) }) @@ -230,14 +230,14 @@ var _ = Describe("Linux", func() { Context("call with existing group", func() { It("should return valid group", func() { linux := NewLinux("/") - group, err := linux.groupLookup("10") + group, err := linux.groupLookup("37") Expect(err).To(BeNil()) Expect(group).NotTo(BeNil()) - Expect(group.Gid).To(Equal("10")) - Expect(group.Name).To(Equal("wheel")) + Expect(group.Gid).To(Equal("37")) + Expect(group.Name).To(Equal("operator")) }) }) @@ -268,7 +268,7 @@ var _ = Describe("Linux", func() { Context("call with existing group", func() { It("should return true", func() { linux := NewLinux("/") - isFound := linux.GroupExists("wheel") + isFound := linux.GroupExists("operator") Expect(isFound).To(BeTrue()) }) }) @@ -294,10 +294,10 @@ var _ = Describe("Linux", func() { Describe("userShell()", func() { Context("call with existing user", func() { - It("should return /bin/ash", func() { + It("should return /bin/bash", func() { linux := NewLinux("/") shell := linux.userShell("root") - Expect(shell).To(Equal("/bin/ash")) + Expect(shell).To(Equal("/bin/bash")) }) }) }) diff --git a/api/linux_users.go b/api/linux_users.go index 5cffeca..e9823c3 100644 --- a/api/linux_users.go +++ b/api/linux_users.go @@ -61,7 +61,7 @@ func init() { // // adduser wants user name be the head and flags the tail. viper.SetDefault("linux_user_add_tpl", "adduser {username} --disabled-password --force-badname --shell {shell}") - viper.SetDefault("linux_user_add_with_gid_tpl", "adduser {username} --disabled-password --force-badname --shell {shell} --group {group}") + viper.SetDefault("linux_user_add_with_gid_tpl", "adduser {username} --disabled-password --force-badname --shell {shell} --gid {group}") viper.SetDefault("linux_user_add_to_group_tpl", "adduser {username} {group}") viper.SetDefault("linux_user_del_tpl", "deluser {username}") } @@ -111,8 +111,8 @@ func (linux *Linux) UserCreate(new linux.User) error { } if new.Gid() != "" { - template = createUserWithGIDCommandTemplate args["gid"] = new.Gid() + template = createUserWithGIDCommandTemplate if primaryGroup, err := linux.groupLookupByID(new.Gid()); err == nil { args["group"] = primaryGroup.Name diff --git a/key_storages/github_keys.go b/key_storages/github_keys.go index 6465aef..83c8e3f 100644 --- a/key_storages/github_keys.go +++ b/key_storages/github_keys.go @@ -52,7 +52,7 @@ func (s *GithubKeys) Get(user string) (value string, err error) { return } - keys, err := s.client.GetKeys(user) + keys, err := s.client.GetKeys(user) if err == nil { @@ -62,7 +62,7 @@ func (s *GithubKeys) Get(user string) (value string, err error) { } value = strings.Join(result, "\n") - } else if err == api.ErrorGitHubNotFound { + } else if err == api.ErrorGitHubNotFound { err = ErrStorageKeyNotFound } else { err = errors.New("Access denied")