diff --git a/contrib/git-sync/Dockerfile b/contrib/git-sync/Dockerfile new file mode 100644 index 00000000000..6929d15b454 --- /dev/null +++ b/contrib/git-sync/Dockerfile @@ -0,0 +1,4 @@ +FROM golang:1.4-onbuild +VOLUME ["/git"] +ENV GIT_SYNC_DEST /git +ENTRYPOINT ["/go/bin/git-sync"] diff --git a/contrib/git-sync/README.md b/contrib/git-sync/README.md new file mode 100644 index 00000000000..f8faa4a0cd1 --- /dev/null +++ b/contrib/git-sync/README.md @@ -0,0 +1,16 @@ +# git-sync + +git-sync is a command that pull a git repository to a local directory. + +It can be used to source a container volume with the content of a git repo. + +## Usage + +``` +# build the container +docker build -t git-sync . +# run the git-sync container +docker run -d GIT_SYNC_REPO=https://github.com/GoogleCloudPlatform/kubernetes -e GIT_SYNC_BRANCH=gh-pages -r HEAD -v /git-data:/git git-sync +# run a nginx container to serve sync'ed content +docker run -d -p 8080:80 -v /git-data:/usr/share/nginx/html nginx +``` diff --git a/contrib/git-sync/demo/README.md b/contrib/git-sync/demo/README.md new file mode 100644 index 00000000000..e2a72465b56 --- /dev/null +++ b/contrib/git-sync/demo/README.md @@ -0,0 +1,30 @@ +# git-blog-demo + +This demo shows how to use the `git-sync` sidekick container along side `volumes` and `volumeMounts` to create a markdown powered blog. + +## How it works + +The pod is composed of 3 containers that share directories using 2 volumes: + +- The `git-sync` container clones a git repo into the `markdown` volume +- The `hugo` container read from the `markdown` volume and render it into the `html` volume. +- The `nginx` container serve the content from the `html` volume. + +## Usage + +Build the demo containers, and push them to a registry + +``` +docker build -t /git-sync .. +docker build -t /hugo hugo/ +docker push /hugo /git-sync +``` + +Create the pod and the service for the blog + +``` +kubectl pods create config/pod.html +kubectl services create config/pod.html +``` + +Open the service external ip in your browser diff --git a/contrib/git-sync/demo/blog/archetypes/.keep b/contrib/git-sync/demo/blog/archetypes/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/blog/config.toml b/contrib/git-sync/demo/blog/config.toml new file mode 100644 index 00000000000..ab3aa9e446e --- /dev/null +++ b/contrib/git-sync/demo/blog/config.toml @@ -0,0 +1,3 @@ +baseurl = "http://example.com" +languageCode = "en-us" +title = "example blog" diff --git a/contrib/git-sync/demo/blog/content/about.md b/contrib/git-sync/demo/blog/content/about.md new file mode 100644 index 00000000000..afb2ac0f4bb --- /dev/null +++ b/contrib/git-sync/demo/blog/content/about.md @@ -0,0 +1,9 @@ ++++ +date = "2014-12-19T15:29:48-08:00" +draft = true +title = "about" ++++ + +## A headline + +Some content about the blog. diff --git a/contrib/git-sync/demo/blog/content/post/first.md b/contrib/git-sync/demo/blog/content/post/first.md new file mode 100644 index 00000000000..630bbbd01d3 --- /dev/null +++ b/contrib/git-sync/demo/blog/content/post/first.md @@ -0,0 +1,9 @@ ++++ +date = "2014-12-19T15:30:18-08:00" +draft = true +title = "first" ++++ + +## first port + +This is the first post. diff --git a/contrib/git-sync/demo/blog/layouts/.keep b/contrib/git-sync/demo/blog/layouts/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/blog/static/.keep b/contrib/git-sync/demo/blog/static/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/git-sync/demo/config/pod.yaml b/contrib/git-sync/demo/config/pod.yaml new file mode 100644 index 00000000000..75ff074c18b --- /dev/null +++ b/contrib/git-sync/demo/config/pod.yaml @@ -0,0 +1,50 @@ +id: blog-pod +kind: Pod +apiVersion: v1beta1 +desiredState: + manifest: + version: v1beta1 + containers: + - name: git-sync + image: proppy/git-sync + imagePullPolicy: PullAlways + env:: + - name: GIT_SYNC_REPO + value: https://github.com/proppy/blog.git + - name: GIT_SYNC_DEST + value: /git + volumeMounts: + - name: markdown + mountPath: /git + - name: hugo + image: proppy/hugo + imagePullPolicy: PullAlways + env: + - name: SRC + value: /src + - name: BUILD_DRAFT + value: 'true' + - name: BASE_URL + value: kube.proppy.sh + volumeMounts: + - name: markdown + mountPath: /src + - name: html + mountPath: /dest + - name: nginx + image: nginx + volumeMounts: + - name: html + mountPath: /usr/share/nginx/html + ports: + - name: http-server + containerPort: 80 + volumes: + - name: markdown + source: + emptyDir: {} + - name: html + source: + emptyDir: {} +labels: + name: blog diff --git a/contrib/git-sync/demo/config/service.yaml b/contrib/git-sync/demo/config/service.yaml new file mode 100644 index 00000000000..646b4c59aa6 --- /dev/null +++ b/contrib/git-sync/demo/config/service.yaml @@ -0,0 +1,8 @@ +id: blog-service +kind: Service +apiVersion: v1beta1 +port: 80 +containerPort: http-server +selector: + name: blog +createExternalLoadBalancer: true diff --git a/contrib/git-sync/demo/hugo/Dockerfile b/contrib/git-sync/demo/hugo/Dockerfile new file mode 100644 index 00000000000..40af5e67ed8 --- /dev/null +++ b/contrib/git-sync/demo/hugo/Dockerfile @@ -0,0 +1,13 @@ +FROM golang +RUN go get -v github.com/spf13/hugo +RUN git clone --recursive https://github.com/spf13/hugoThemes.git /themes +VOLUME ["/src", "/dest"] +EXPOSE 1313 +ENV SRC /src +ENV DEST /dest +ENV THEME hyde +ENV BUILD_DRAFT false +ENV BASE_URL "" +ADD run-hugo /run-hugo +ENTRYPOINT ["/run-hugo"] +CMD ["server", "--source=${SRC}", "--theme=${THEME}", "--buildDrafts=${BUILD_DRAFT}", "--baseUrl=${BASE_URL}", "--watch", "--destination=${DEST}", "--appendPort=false"] diff --git a/contrib/git-sync/demo/hugo/run-hugo b/contrib/git-sync/demo/hugo/run-hugo new file mode 100755 index 00000000000..37636025523 --- /dev/null +++ b/contrib/git-sync/demo/hugo/run-hugo @@ -0,0 +1,21 @@ +#!/bin/bash + +# Copyright 2014 Google Inc. All rights reserved. +# +# 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 +# +# 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. + +set -ex +if [ ! -d ${SRC}/themes ]; then + ln -s /themes ${SRC}/themes +fi +hugo $(eval echo $*) # force default CMD env expansion diff --git a/contrib/git-sync/main.go b/contrib/git-sync/main.go new file mode 100644 index 00000000000..7667e8df7a5 --- /dev/null +++ b/contrib/git-sync/main.go @@ -0,0 +1,112 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +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 + +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. +*/ + +// git-sync is a command that pull a git repository to a local directory. + +package main // import "github.com/GoogleCloudPlatform/kubernetes/contrib/git-sync" + +import ( + "flag" + "fmt" + "log" + "os" + "os/exec" + "path" + "strconv" + "strings" + "time" +) + +var flRepo = flag.String("repo", envString("GIT_SYNC_REPO", ""), "git repo url") +var flBranch = flag.String("branch", envString("GIT_SYNC_BRANCH", "master"), "git branch") +var flRev = flag.String("rev", envString("GIT_SYNC_BRANCH", "HEAD"), "git rev") +var flDest = flag.String("dest", envString("GIT_SYNC_DEST", ""), "destination path") +var flWait = flag.Int("wait", envInt("GIT_SYNC_WAIT", 0), "number of seconds to wait before exit") + +func envString(key, def string) string { + if env := os.Getenv(key); env != "" { + return env + } + return def +} + +func envInt(key string, def int) int { + if env := os.Getenv(key); env != "" { + val, err := strconv.Atoi(env) + if err != nil { + log.Println("invalid value for %q: using default: %q", key, def) + return def + } + return val + } + return def +} + +const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_BRANCH= GIT_SYNC_WAIT=] git-sync -repo GIT_REPO_URL -dest PATH [-branch -wait]" + +func main() { + flag.Parse() + if *flRepo == "" || *flDest == "" { + flag.Usage() + log.Fatal(usage) + } + if _, err := exec.LookPath("git"); err != nil { + log.Fatalf("required git executable not found: %v", err) + } + if err := syncRepo(*flRepo, *flDest, *flBranch, *flRev); err != nil { + log.Fatalf("error syncing repo: %v", err) + } + log.Printf("wait %d seconds", *flWait) + time.Sleep(time.Duration(*flWait) * time.Second) + log.Println("done") +} + +// syncRepo syncs the branch of a given repository to the destination at the given rev. +func syncRepo(repo, dest, branch, rev string) error { + gitRepoPath := path.Join(dest, ".git") + _, err := os.Stat(gitRepoPath) + switch { + case os.IsNotExist(err): + // clone repo + cmd := exec.Command("git", "clone", "--no-checkout", "-b", branch, repo, dest) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error cloning repo %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output)) + } + log.Printf("clone %q: %s", repo, string(output)) + case err != nil: + return fmt.Errorf("error checking if repo exist %q: %v", gitRepoPath, err) + } + + // fetch branch + cmd := exec.Command("git", "fetch", "origin", branch) + cmd.Dir = dest + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error running command %q: %v: %s", strings.Join(cmd.Args, " "), err, string(output)) + } + log.Printf("fetch %q: %s", branch, string(output)) + + // reset working copy + cmd = exec.Command("git", "reset", "--hard", rev) + cmd.Dir = dest + output, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error running command %q : %v: %s", strings.Join(cmd.Args, " "), err, string(output)) + } + log.Printf("reset %q: %v", rev, string(output)) + return nil +}