diff --git a/.dapper b/.dapper new file mode 100755 index 0000000..3d1b26a Binary files /dev/null and b/.dapper differ diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6e43c2a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +./bin +./.dapper +./dist +./.trash-cache diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a252c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.dapper +/bin +/dist +*.swp +/.trash-cache +/.vscode diff --git a/Dockerfile.dapper b/Dockerfile.dapper new file mode 100644 index 0000000..b718eff --- /dev/null +++ b/Dockerfile.dapper @@ -0,0 +1,31 @@ +FROM ubuntu:16.04 +# FROM arm=armhf/ubuntu:16.04 + +ARG DAPPER_HOST_ARCH=amd64 +ENV HOST_ARCH=${DAPPER_HOST_ARCH} ARCH=${DAPPER_HOST_ARCH} + +RUN apt-get update && \ + apt-get install -y gcc ca-certificates git wget curl vim less file && \ + rm -f /bin/sh && ln -s /bin/bash /bin/sh + +ENV GOLANG_ARCH_amd64=amd64 GOLANG_ARCH_arm=armv6l GOLANG_ARCH=GOLANG_ARCH_${ARCH} \ + GOPATH=/go PATH=/go/bin:/usr/local/go/bin:${PATH} SHELL=/bin/bash + +RUN wget -O - https://storage.googleapis.com/golang/go1.7.3.linux-${!GOLANG_ARCH}.tar.gz | tar -xzf - -C /usr/local && \ + go get github.com/rancher/trash && go get github.com/golang/lint/golint + +ENV DOCKER_URL_amd64=https://get.docker.com/builds/Linux/x86_64/docker-1.10.3 \ + DOCKER_URL_arm=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm \ + DOCKER_URL=DOCKER_URL_${ARCH} + +RUN wget -O - ${!DOCKER_URL} > /usr/bin/docker && chmod +x /usr/bin/docker + +ENV DAPPER_SOURCE /go/src/github.com/rancher/container-crontab/ +ENV DAPPER_OUTPUT ./bin ./dist +ENV DAPPER_DOCKER_SOCKET true +ENV TRASH_CACHE ${DAPPER_SOURCE}/.trash-cache +ENV HOME ${DAPPER_SOURCE} +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./scripts/entry"] +CMD ["ci"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7d72a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +TARGETS := $(shell ls scripts) + +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-`uname -s`-`uname -m` > .dapper.tmp + @@chmod +x .dapper.tmp + @./.dapper.tmp -v + @mv .dapper.tmp .dapper + +$(TARGETS): .dapper + ./.dapper $@ + +trash: .dapper + ./.dapper -m bind trash + +trash-keep: .dapper + ./.dapper -m bind trash -k + +deps: trash + +.DEFAULT_GOAL := ci + +.PHONY: $(TARGETS) diff --git a/README.md b/README.md new file mode 100644 index 0000000..259b9ed --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +container-crontab +======== + +A microservice that will perform actions on a Docker container based on cron schedule. + +## Building + +`make` + +## Running + +`./bin/container-crontab` + +## Usage + +Once `container-crontab` is up and running it watches Docker socket events for `create, start and destroy` events. +If a container is found to have the label `cron.schedule` then it will be added to the crontab based on the schedule. + +Cron scheduling rules follow: [Expression Format](https://godoc.org/github.com/robfig/cron#hdr-CRON_Expression_Format) + +## Override labels that can be applied + +To override the default start action on the container, set the label `cron.action` equal to ``stop` or `restart`. + +To override the default 10 second restart/stop timeout set the label `cron.restart_timeout` to the number of +seconds you would like. For instance for 20 seconds: `cron.restart_timeout=20`. + +## Examples +``` +# Restart every minute +> docker run -d --label=cron.schedule="0 * * * * ?" ubuntu:16.04 date +``` + +## License +Copyright (c) 2014-2017 [Rancher Labs, Inc.](http://rancher.com) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/bin/clepsydra b/bin/clepsydra new file mode 100755 index 0000000..24a8721 Binary files /dev/null and b/bin/clepsydra differ diff --git a/cron/cron.go b/cron/cron.go new file mode 100644 index 0000000..dbf3d28 --- /dev/null +++ b/cron/cron.go @@ -0,0 +1,68 @@ +package cron + +import ( + "github.com/Sirupsen/logrus" + "github.com/robfig/cron" +) + +type cronJob interface { + Deactivate() +} + +// Crontab is the struct that holds the cron runner +type Crontab struct { + cronRunner *cron.Cron + jobs map[string]cronJob +} + +// NewCrontab creates the crontab +func NewCrontab() (*Crontab, error) { + logrus.Infof("Starting Cron") + crontab := &Crontab{ + cronRunner: cron.New(), + jobs: map[string]cronJob{}, + } + + crontab.cronRunner.Start() + + return crontab, nil +} + +// GetEntries lists the cron entries +func (ct *Crontab) GetEntries() []*cron.Entry { + entries := ct.cronRunner.Entries() + return entries +} + +// AddJob Adds a docker job to the crontab +func (ct *Crontab) AddJob(id string, labels map[string]string, jobType string) error { + var schedule string + var job cron.Job + + if ct.jobs[id] != nil { + logrus.Debugf("Job %s, already exists", id) + return nil + } + + if sched, ok := labels["cron.schedule"]; ok { + schedule = sched + } + + switch jobType { + case "docker": + dj := NewDockerJob(id, labels) + ct.jobs[id] = dj + job = dj + default: + logrus.Warnf("Unknown job type: %s", jobType) + } + + return ct.cronRunner.AddJob(schedule, job) +} + +// RemoveJob remove a docker job from the cron queue +func (ct *Crontab) RemoveJob(id string) { + if job, ok := ct.jobs[id]; ok { + job.Deactivate() + } +} diff --git a/cron/docker.go b/cron/docker.go new file mode 100644 index 0000000..4f2b42b --- /dev/null +++ b/cron/docker.go @@ -0,0 +1,131 @@ +package cron + +import ( + "context" + "strconv" + "time" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" +) + +// DockerJob implements the cron job interface +type DockerJob struct { + ID string + Action string + Schedule string + Leader bool + Active bool + lastError error + restartTimeout time.Duration +} + +// Err returns last error message +func (dj *DockerJob) Err() error { + return dj.lastError +} + +// Run Implements the job interface from cron package +func (dj *DockerJob) Run() { + defer dj.resetErr() + + if dj.Active { + logrus.Debugf("Executing: %s on %s", dj.Action, dj.ID) + switch dj.Action { + case "start": + dj.start() + case "restart": + dj.restart() + case "stop": + dj.stop() + default: + logrus.Errorf("Unsupported action: %s for container id: %s", dj.Action, dj.ID) + } + } + + if dj.Err() != nil { + logrus.Error(dj.Err()) + } +} + +func (dj *DockerJob) resetErr() { + if dj.lastError != nil { + logrus.Debugf("Reseting error on %s", dj.ID) + } + dj.lastError = nil +} + +func (dj *DockerJob) start() { + var client *client.Client + client, dj.lastError = getDockerClient() + + if dj.Err() == nil { + dj.lastError = client.ContainerStart(context.Background(), dj.ID, types.ContainerStartOptions{}) + } +} + +func (dj *DockerJob) restart() { + var client *client.Client + client, dj.lastError = getDockerClient() + + if dj.Err() == nil { + dj.lastError = client.ContainerRestart(context.Background(), dj.ID, &dj.restartTimeout) + } +} + +func (dj *DockerJob) stop() { + var client *client.Client + client, dj.lastError = getDockerClient() + + if dj.Err() == nil { + dj.lastError = client.ContainerStop(context.Background(), dj.ID, &dj.restartTimeout) + } +} + +func getDockerClient() (*client.Client, error) { + return client.NewClient("unix:///var/run/docker.sock", "1.22", nil, map[string]string{}) +} + +// NewDockerJob creates a DockerJob and sets defaults +func NewDockerJob(id string, labels map[string]string) *DockerJob { + dj := &DockerJob{ + ID: id, + Schedule: labels["cron.schedule"], + Action: "start", + Leader: false, + Active: true, + lastError: nil, + restartTimeout: getDuration(10), + } + + if value, ok := labels["cron.action"]; ok { + dj.Action = value + } + + if _, ok := labels["cron.leader"]; ok { + dj.Leader = true + } + + if TO, ok := labels["cron.restart_timeout"]; ok { + i, err := strconv.Atoi(TO) + if err != nil { + logrus.Error("Error converting cron.restart_timeout to int, sticking with default of 10seconds") + logrus.Error(err) + i = 10 + } + dj.restartTimeout = getDuration(i) + } + + return dj +} + +func getDuration(i int) time.Duration { + return time.Duration(i) * time.Second +} + +// Deactivate Sets the Active attribute to false. This will skip running +func (dj *DockerJob) Deactivate() { + dj.Active = false + logrus.Debugf("Deactivating: %s", dj.ID) +} diff --git a/events/events.go b/events/events.go new file mode 100644 index 0000000..8f7dd74 --- /dev/null +++ b/events/events.go @@ -0,0 +1,140 @@ +package events + +import ( + "context" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/events" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" + "github.com/rancher/container-crontab/cron" +) + +// Router Interface +type Router interface { + Listen() (<-chan events.Message, <-chan error) +} + +// Handler handles messages +type Handler interface { + Handle(Message) +} + +// Message is a message from an event stream +type Message *events.Message + +// DockerHandler handles docker messages +type DockerHandler struct { + crontab *cron.Crontab +} + +// NewDockerHandler returns a docker handler with crontab +func NewDockerHandler() *DockerHandler { + crontab, _ := cron.NewCrontab() + + dClient, err := client.NewClient("unix:///var/run/docker.sock", "1.22", nil, map[string]string{}) + if err != nil { + logrus.Fatal(err) + return nil + } + + containers, err := dClient.ContainerList(context.Background(), types.ContainerListOptions{ + All: true, + }) + if err != nil { + logrus.Fatal(err) + return nil + } + + // Scan containers + logrus.Infof("Scanning for container cron entries") + for _, container := range containers { + success := true + if schedule, ok := container.Labels["cron.schedule"]; ok { + err = crontab.AddJob(container.ID, container.Labels, "docker") + if err != nil { + logrus.Errorf("error adding: %s. Got: %s", container.ID, err) + success = false + } + + if success { + logrus.Infof("Added: %s, with schedule: %s", container.ID, schedule) + } + } + } + + return &DockerHandler{ + crontab: crontab, + } +} + +// Handle implements handler interface +func (dh DockerHandler) Handle(msg Message) { + // Adding a cron.schedule label flags the container for deeper inspection + // With this service + if _, ok := msg.Actor.Attributes["cron.schedule"]; ok { + if msg.Action == "start" || msg.Action == "create" { + logrus.Debugf("Processing %s event for container: %s", msg.Action, msg.ID) + dh.crontab.AddJob(msg.ID, msg.Actor.Attributes, "docker") + } + + if msg.Action == "destroy" { + logrus.Debugf("Processing destroy event for container: %s", msg.ID) + dh.crontab.RemoveJob(msg.ID) + } + } + + dh.crontab.GetEntries() +} + +//DockerEventRouter is the Docker event handler implementation +type DockerEventRouter struct { + DockerClient *client.Client +} + +// NewEventRouter returns the a Docker event handler +func NewEventRouter() (Router, error) { + dClient, err := client.NewClient("unix:///var/run/docker.sock", "1.22", nil, map[string]string{}) + if err != nil { + return nil, err + } + return DockerEventRouter{ + DockerClient: dClient, + }, nil +} + +// StartRouter calls the listener function and takes the interface for testing +func StartRouter(router Router) { + handler := NewDockerHandler() + for { + eventStream, errChan := router.Listen() + select { + case event := <-eventStream: + handler.Handle(&event) + case err := <-errChan: + logrus.Error(err) + } + } +} + +// Listen implements the Router interface +func (de DockerEventRouter) Listen() (<-chan events.Message, <-chan error) { + filterArgs := filters.NewArgs() + // Adds the cron job + filterArgs.Add("event", "start") + filterArgs.Add("event", "create") + + // Not sure that we need these...unknown usecases + // filterArgs.Add("event", "stop") + // filterArgs.Add("event", "die") + + // removes from the cron queue + filterArgs.Add("event", "destroy") + + eventOptions := types.EventsOptions{ + Filters: filterArgs, + } + + return de.DockerClient.Events(context.Background(), eventOptions) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..3d420c1 --- /dev/null +++ b/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "os" + + "github.com/Sirupsen/logrus" + "github.com/rancher/container-crontab/events" + "github.com/urfave/cli" +) + +// VERSION of the application +var VERSION = "v0.0.0-dev" + +func beforeApp(c *cli.Context) error { + if c.GlobalBool("debug") { + logrus.SetLevel(logrus.DebugLevel) + } + return nil +} + +func main() { + app := cli.NewApp() + app.Name = "container-crontab" + app.Version = VERSION + app.Usage = "container-crontab" + app.Action = start + app.Before = beforeApp + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug,d", + }, + } + + app.Run(os.Args) +} + +func start(c *cli.Context) error { + router, err := events.NewEventRouter() + if err != nil { + logrus.Fatal(err) + } + + events.StartRouter(router) + + return nil +} diff --git a/package/Dockerfile b/package/Dockerfile new file mode 100644 index 0000000..c7a94fb --- /dev/null +++ b/package/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.4 +COPY container-crontab /usr/bin/ +CMD ["container-crontab"] diff --git a/scripts/build b/scripts/build new file mode 100755 index 0000000..3be1c63 --- /dev/null +++ b/scripts/build @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +source $(dirname $0)/version + +cd $(dirname $0)/.. + +mkdir -p bin +[ "$(uname)" != "Darwin" ] && LINKFLAGS="-linkmode external -extldflags -static -s" +go build -ldflags "-X main.VERSION=$VERSION $LINKFLAGS" -o bin/container-crontab diff --git a/scripts/ci b/scripts/ci new file mode 100755 index 0000000..e6fae79 --- /dev/null +++ b/scripts/ci @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +cd $(dirname $0) + +./build +./test +./validate +./package diff --git a/scripts/entry b/scripts/entry new file mode 100755 index 0000000..78fb567 --- /dev/null +++ b/scripts/entry @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +mkdir -p bin dist +if [ -e ./scripts/$1 ]; then + ./scripts/"$@" +else + exec "$@" +fi + +chown -R $DAPPER_UID:$DAPPER_GID . diff --git a/scripts/package b/scripts/package new file mode 100755 index 0000000..1a142b4 --- /dev/null +++ b/scripts/package @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +source $(dirname $0)/version + +ARCH=${ARCH:?"ARCH not set"} +SUFFIX="" +[ "${ARCH}" != "amd64" ] && SUFFIX="_${ARCH}" + +cd $(dirname $0)/../package + +TAG=${TAG:-${VERSION}${SUFFIX}} +REPO=${REPO:-rancher} + +cp ../bin/container-crontab . +docker build -t ${REPO}/container-crontab:${TAG} . + +echo Built ${REPO}/container-crontab:${TAG} diff --git a/scripts/release b/scripts/release new file mode 100755 index 0000000..7af0df3 --- /dev/null +++ b/scripts/release @@ -0,0 +1,3 @@ +#!/bin/bash + +exec $(dirname $0)/ci diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..78d0797 --- /dev/null +++ b/scripts/test @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +echo Running tests + +PACKAGES=". $(find -name '*.go' | xargs -I{} dirname {} | cut -f2 -d/ | sort -u | grep -Ev '(^\.$|.git|.trash-cache|vendor|bin)' | sed -e 's!^!./!' -e 's!$!/...!')" + +[ "${ARCH}" == "amd64" ] && RACE=-race +go test ${RACE} -cover -tags=test ${PACKAGES} diff --git a/scripts/validate b/scripts/validate new file mode 100755 index 0000000..17f8372 --- /dev/null +++ b/scripts/validate @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +echo Running validation + +PACKAGES=". $(find -name '*.go' | xargs -I{} dirname {} | cut -f2 -d/ | sort -u | grep -Ev '(^\.$|.git|.trash-cache|vendor|bin)' | sed -e 's!^!./!' -e 's!$!/...!')" + +echo Running: go vet +go vet ${PACKAGES} +echo Running: golint +for i in ${PACKAGES}; do + if [ -n "$(golint $i | grep -v 'should have comment.*or be unexported' | tee /dev/stderr)" ]; then + failed=true + fi +done +test -z "$failed" +echo Running: go fmt +test -z "$(go fmt ${PACKAGES} | tee /dev/stderr)" diff --git a/scripts/version b/scripts/version new file mode 100755 index 0000000..c52f0f7 --- /dev/null +++ b/scripts/version @@ -0,0 +1,18 @@ +#!/bin/bash + +if [ -n "$(git status --porcelain --untracked-files=no)" ]; then + DIRTY="-dirty" +fi + +COMMIT=$(git rev-parse --short HEAD) +GIT_TAG=$(git tag -l --contains HEAD | head -n 1) + +if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then + VERSION=$GIT_TAG +else + VERSION="${COMMIT}${DIRTY}" +fi + +if [ -z "$ARCH" ]; then + ARCH=amd64 +fi diff --git a/trash.conf b/trash.conf new file mode 100644 index 0000000..7387803 --- /dev/null +++ b/trash.conf @@ -0,0 +1,18 @@ +# package +github.com/rancher/container-crontab + +github.com/Sirupsen/logrus v0.10.0 +github.com/urfave/cli v1.19.1 + +github.com/docker/docker/api/types 8a8416ccbdaf08598f4ac38255e4267859682bb9 +github.com/docker/docker/client 8a8416ccbdaf08598f4ac38255e4267859682bb9 +github.com/docker/docker/pkg/tlsconfig 8a8416ccbdaf08598f4ac38255e4267859682bb9 +github.com/docker/distribution v2.5.1 +github.com/docker/go-connections 990a1a1a70b0da4c4cb70e117971a4f0babfbf1a +github.com/docker/go-units f2d77a61e3c169b43402a0a1e84f06daf29b8190 +github.com/opencontainers/runc/libcontainer/user master +github.com/pkg/errors v0.8.0 +github.com/robfig/cron master +golang.org/x/net/context master +golang.org/x/net/context/ctxhttp master +golang.org/x/net/proxy master