luisf 8 years ago
commit
6d5278f103

+ 6 - 0
.dockerignore

@@ -0,0 +1,6 @@
+.deps
+bin
+pkg
+builder
+DIST
+dist.tar.gz

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.deps
+bin
+pkg
+builder
+DIST
+dist.tar.gz

+ 26 - 0
Makefile

@@ -0,0 +1,26 @@
+
+
+GOPATH=${CURDIR}/.deps:${CURDIR}
+
+
+all: 
+	go build -o DIST/builder buildme/cmd/builder
+
+deps: 
+	go get -v buildme/cmd/builder
+
+dist: 
+	tar -c DIST |gzip >dist.tar.gz
+
+
+clean:
+	rm -f dist.tar.gz
+	rm -rf DIST 
+
+distclean: clean
+	rm -rf .deps
+	rm -rf bin
+	rm -rf pkg
+
+
+.PHONY: all deps clean distclean

+ 7 - 0
buildme.yml

@@ -0,0 +1,7 @@
+BASE: golang/1.8.1-alpine
+PREPARE:
+  - apk add --update make
+  - apk add --update git
+BUILD:
+  - make dist
+

+ 10 - 0
docker/Dockerfile.build

@@ -0,0 +1,10 @@
+FROM golang:1.8.1-alpine
+RUN apk add --update make
+RUN apk add --update git
+
+ADD . /buildme
+WORKDIR /buildme
+
+RUN make deps
+
+CMD make dist

+ 13 - 0
docker/Dockerfile.build2

@@ -0,0 +1,13 @@
+#Should be called DIST generator
+
+FROM golang:1.8.1
+
+ADD src/buildme $GOPATH/src/buildme/
+WORKDIR $GOPATH
+
+# Build steps
+RUN go get -v buildme/cmd/builder
+RUN CGO_ENABLED=0 go build -o DIST/builder buildme/cmd/builder
+
+# Generate img
+CMD tar -cz - bin/builder

+ 1 - 0
env.sh

@@ -0,0 +1 @@
+export GOPATH=$(pwd)/.deps:$(pwd)

+ 15 - 0
src/buildme/README.md

@@ -0,0 +1,15 @@
+buildme
+=====================
+
+Automate builds
+
+
+
+###Fetcher
+Fetcher operation:
+
+1. Fetches project
+2. Return a tar file reader
+
+
+

+ 117 - 0
src/buildme/buildme.go

@@ -0,0 +1,117 @@
+/* buildme context */
+package buildme
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"os/exec"
+
+	"dev.hexasoftware.com/hxs/prettylog"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	docker "github.com/docker/docker/client"
+)
+
+var (
+	//ErrFetcherNotFound In case fetcher is not found
+	ErrFetcherNotFound = errors.New("Fetcher not found")
+	log                = prettylog.New("buildme")
+)
+
+//BuildMe context
+/*type BuildMe struct{}
+
+//New buildme context
+func New() *BuildMe {
+	return &BuildMe{}
+}*/
+
+//Fetch fetches a Tar file from path/url
+func Fetch(ftype string, fpath string) (*Project, error) {
+	fetcher, ok := fetchers[ftype]
+	if !ok {
+		return nil, ErrFetcherNotFound
+	}
+	log.Println("Fetching:", fpath)
+
+	return fetcher.Fetch(fpath)
+}
+
+// Build Fetch url/path and run docker build
+///////////////////////////////////////////////////////////////// BUILD
+func Build(ftype string, fpath string) error {
+	var err error
+
+	// Fetch proj from tar
+	proj, err := Fetch(ftype, fpath)
+	if err != nil {
+		return err
+	}
+	defer proj.Close()
+
+	//addDockerfile(proj)
+
+	log.Println("New docker environ")
+	cli, err := docker.NewEnvClient()
+	if err != nil {
+		return err
+	}
+
+	dockertag := fmt.Sprintf("%s-builder", proj.Name)
+	log.Println("Building and tagging:", dockertag)
+	///////////////
+	/// DOCKER OPERATION / BUILDER operation
+	ctx := context.Background()
+	{
+		log.Println("Docker image build dockerfile:", proj.Dockerfile)
+		imageRes, err := cli.ImageBuild(ctx, proj, types.ImageBuildOptions{Tags: []string{dockertag}, Dockerfile: proj.Dockerfile})
+		if err != nil {
+			return err
+		}
+		defer imageRes.Body.Close()
+		io.Copy(os.Stdout, imageRes.Body) // Do some treatment somehow
+		//inf, err := cli.Info(ctx)
+		//log.Println("Info:", inf, err)
+	}
+
+	{
+
+		log.Println("Testing running the builder")
+		containerName := dockertag
+		containerCfg := &container.Config{AttachStdout: true, Cmd: []string{"/bin/sh", "-c", "echo $CMD"}}
+		res, err := cli.ContainerCreate(ctx, containerCfg, nil, nil, containerName)
+		if err != nil {
+			return err
+		}
+		log.Println("Created container ID:", res)
+
+		/*log.Println("Starting container test")
+		err = cli.ContainerStart(ctx, res.ID, types.ContainerStartOptions{AttachStdout: true})
+		if err != nil {
+			return err
+		}*/
+
+		log.Println("Please execute docker run", containerName, "> dist.tar.gz")
+		log.Println("Trying to remove container", res)
+		err = cli.ContainerRemove(ctx, res.ID, types.ContainerRemoveOptions{})
+		if err != nil {
+			return err
+		}
+	}
+
+	//////////////////////
+	// Docker run builder
+
+	return nil
+}
+
+func mexec(cmdname string, args ...string) {
+	cmd := exec.Command(cmdname, args...)
+	cmd.Stderr = os.Stderr
+	cmd.Stdout = os.Stdout
+	cmd.Run()
+}

+ 18 - 0
src/buildme/buildme_test.go

@@ -0,0 +1,18 @@
+package buildme_test
+
+import (
+	"buildme"
+	"buildme/internal/builder"
+	"testing"
+
+	"dev.hexasoftware.com/hxs/prettylog"
+)
+
+func TestBuildMe(t *testing.T) {
+	prettylog.Global()
+	t.Log("Testing builder")
+
+	bm := buildme.New()
+	bm.AddBuilder(buildme.Type("git"), &builder.Git{})
+	bm.Build("git", "http://dev.hexasoftware.com/stdio/sampleapp.git")
+}

+ 17 - 0
src/buildme/cmd/server/main.go

@@ -0,0 +1,17 @@
+package main
+
+import (
+	"buildme"
+	"log"
+
+	"dev.hexasoftware.com/hxs/prettylog"
+)
+
+func main() {
+	prettylog.Global()
+	log.Println("Testing builder")
+
+	bm := buildme.New()
+	bm.AddBuilder(buildme.Type("git"), &builder.Git{})
+	bm.Build("git", "http://dev.hexasoftware.com/stdio/sampleapp.git")
+}

+ 15 - 0
src/buildme/fetcher.go

@@ -0,0 +1,15 @@
+package buildme
+
+var (
+	fetchers = map[string]Fetcher{}
+)
+
+func RegisterFetcher(ftype string, fetcher Fetcher) {
+	fetchers[ftype] = fetcher
+}
+
+//Fetcher goal of fetcher is to fetch a URL/Repositor/Path and place it in temporary dir
+type Fetcher interface {
+	//Fetch should return a tar reader, projname or error
+	Fetch(fpath string) (*Project, error)
+}

+ 55 - 0
src/buildme/fetcher/git/git.go

@@ -0,0 +1,55 @@
+package git
+
+import (
+	"buildme/utils"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+
+	"buildme"
+
+	"dev.hexasoftware.com/hxs/prettylog"
+	git "gopkg.in/src-d/go-git.v4"
+)
+
+var (
+	log = prettylog.New("GitFetcher")
+)
+
+func init() {
+	// Register fetcher
+	buildme.RegisterFetcher("git", &Git{})
+}
+
+type Git struct {
+}
+
+// Fetcher return a project
+func (g *Git) Fetch(fpath string) (*buildme.Project, error) {
+	log.Println("Building git repo:", fpath)
+
+	projName := filepath.Base(fpath)
+
+	tmpdir, err := ioutil.TempDir("/tmp", "buildme.")
+	if err != nil {
+		return nil, err
+	}
+
+	log.Println("Cloning to:", tmpdir)
+	git.PlainClone(tmpdir, false, &git.CloneOptions{
+		URL:      fpath,
+		Progress: os.Stdout,
+	})
+
+	log.Println("Taring content")
+
+	/*tarfile, err := ioutil.TempFile("/tmp", "tarbuildme.")
+	reader, err := utils.TarDir(tmpdir, tarfile)*/
+
+	tarReader, err := utils.TempTar(tmpdir, os.TempDir(), "buildme.tar")
+
+	log.Println("Removing dir", tmpdir)
+	os.RemoveAll(tmpdir)
+
+	return &buildme.Project{tarReader, projName, "docker/Dockerfile.build"}, nil
+}

+ 48 - 0
src/buildme/fetcher/path/path.go

@@ -0,0 +1,48 @@
+package path
+
+import (
+	"buildme"
+	"buildme/utils"
+	"errors"
+	"os"
+	"path/filepath"
+
+	"dev.hexasoftware.com/hxs/prettylog"
+)
+
+var (
+	log = prettylog.New("PathFetcher")
+)
+
+func init() {
+	buildme.RegisterFetcher("path", &Path{})
+}
+
+type Path struct{}
+
+func (p *Path) Fetch(fpath string) (*buildme.Project, error) {
+
+	fpath, err := filepath.Abs(fpath)
+	if err != nil {
+		return nil, err
+	}
+
+	s, err := os.Stat(fpath)
+	if err != nil {
+		return nil, err
+	}
+	if !s.IsDir() {
+		return nil, errors.New("Path should be a directory")
+	}
+	log.Println("Building path:", fpath)
+	projName := filepath.Base(fpath)
+
+	tmpTarReader, err := utils.Tar(fpath, nil) // in memory
+	//tmpTarReader, err := utils.TempTar(fpath, os.TempDir(), "tarbuildme.")
+	if err != nil {
+		return nil, err
+	}
+
+	return &buildme.Project{tmpTarReader, projName, "docker/Dockerfile.build"}, nil
+
+}

+ 10 - 0
src/buildme/project.go

@@ -0,0 +1,10 @@
+package buildme
+
+import "io"
+
+// Project type
+type Project struct {
+	io.ReadCloser // Tar format? tar reader perhaps
+	Name          string
+	Dockerfile    string
+}

+ 39 - 0
src/buildme/utils/caller.go

@@ -0,0 +1,39 @@
+package utils
+
+import (
+	"fmt"
+	"runtime"
+	"strings"
+)
+
+func Caller(p ...int) string {
+	fns := 2
+	if len(p) > 0 {
+		fns = p[0]
+	}
+
+	fptr, fname, line, ok := runtime.Caller(fns)
+	if !ok {
+		return ""
+	}
+	fn := runtime.FuncForPC(fptr)
+	return fmt.Sprintf("%s/%s:%d", fname, fn.Name(), line)
+}
+
+// Stack Return stack maximum 10
+func Stack() string {
+
+	stack := []string{"STACK"}
+	for i := 1; i < 10; i++ {
+		fptr, _, line, ok := runtime.Caller(i)
+		if !ok || i > 10 {
+			break
+		}
+		fn := runtime.FuncForPC(fptr)
+
+		stack = append(stack, fmt.Sprintf("%s:%d", fn.Name(), line))
+	}
+
+	return strings.Join(stack, "\n")
+
+}

+ 14 - 0
src/buildme/utils/noprwcloser.go

@@ -0,0 +1,14 @@
+package utils
+
+import "io"
+
+type nopRWCloser struct {
+	io.ReadWriter
+}
+
+func (*nopRWCloser) Close() error {
+	return nil
+}
+func NopRWCloser(rw io.ReadWriter) io.ReadWriteCloser {
+	return &nopRWCloser{rw}
+}

+ 116 - 0
src/buildme/utils/tardir.go

@@ -0,0 +1,116 @@
+package utils
+
+import (
+	"archive/tar"
+	"bytes"
+	"errors"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+//Tar tar a folder and return a reader?
+func Tar(fpath string, f *os.File) (io.ReadWriteCloser, error) {
+	var err error
+
+	var w io.ReadWriteCloser
+	if f == nil { // if not file we do memory
+		w = NopRWCloser(&bytes.Buffer{})
+	} else {
+		w = f
+	}
+
+	if !filepath.IsAbs(fpath) {
+		return nil, errors.New("Path must be absolute")
+	}
+	// Add slash to path end so files can be relative
+	if fpath[len(fpath)-1] != '/' {
+		fpath += "/"
+	}
+
+	tw := tar.NewWriter(w)
+	err = filepath.Walk(fpath, func(p string, info os.FileInfo, err error) error {
+		if info.IsDir() && info.Name()[0] == '.' {
+			return filepath.SkipDir
+		}
+		if info.IsDir() || info.Name()[0] == '.' { // Skip file/dir
+			return nil
+		}
+
+		npath := strings.TrimPrefix(p, fpath)
+		theader, err := tar.FileInfoHeader(info, "")
+		if err != nil {
+			return err
+		}
+		theader.Name = npath // Relative path to root
+
+		// Write file header
+		err = tw.WriteHeader(theader)
+		if err != nil {
+			return err
+		}
+
+		f, err := os.Open(p)
+		if err != nil {
+			return err
+		}
+
+		_, err = io.Copy(tw, f)
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+	tw.Flush()
+	if f != nil {
+		f.Seek(0, os.SEEK_SET)
+	}
+
+	return w, nil
+}
+
+//TempTar will delete temporary tar file on close
+func TempTar(fpath string, tmppath string, prefix string) (io.ReadCloser, error) {
+
+	tarFile, err := ioutil.TempFile(tmppath, prefix)
+	if err != nil {
+		return nil, err
+	}
+
+	tarReader, err := Tar(fpath, tarFile)
+	if err != nil {
+		return nil, err
+	}
+
+	return &tarCloseDelete{tarReader, tarFile.Name()}, nil
+
+}
+
+// Hepler to Delete tar on close
+type tarCloseDelete struct {
+	io.ReadCloser
+	tarFileName string
+}
+
+func (t *tarCloseDelete) Close() error {
+	err := t.ReadCloser.Close()
+	if err != nil {
+		return err
+	}
+	if t.tarFileName == "" {
+		return nil
+	}
+
+	err = os.Remove(t.tarFileName)
+	if err != nil {
+		return err
+	}
+	t.tarFileName = "" // Prevents double remove
+	// Remove tar file if necessary
+	return nil
+}