func tagAndPushImage(c *workflow.Context) (err error) { // Tag and push to registry output.WriteHeader("Pushing image " + image + " to registry") cmd := exec.Command("docker", "push", image) err = cmd.Run() if err != nil { output.WriteError("Error in push: " + err.Error()) stderr, err := cmd.StderrPipe() if err != nil { output.WriteData("Cannot get debug information") return err } spew := bufio.NewReader(stderr) for { line, _, err := spew.ReadLine() if err == io.EOF { return err } if err != nil { output.WriteData(err) return err } output.WriteData(string(line)) } } output.WriteData("done") return }
func deployImage(c *workflow.Context) (err error) { // Report information about the build output.WriteHeader("Deploying") build := &datatypes.Build{ App: os.Getenv("REPO"), ID: buildid, Image: image, User: os.Getenv("USER"), } unitSlice := []*unit.UnitOption{ {"Unit", "Description", "Flitter app " + repo + " deploy " + build.ID}, {"Service", "TimeoutStartSec", "30m"}, {"Service", "ExecStartPre", "/usr/bin/docker pull " + build.Image}, {"Service", "ExecStartPre", "-/usr/bin/docker rm -f app-" + repo + "-" + build.ID}, {"Service", "ExecStart", "/bin/sh -c '/usr/bin/docker run -P --name app-" + repo + "-" + build.ID + " --hostname " + repo + " -e HOST=$COREOS_PRIVATE_IPV4 " + build.Image + " '"}, {"Service", "ExecStop", "/usr/bin/docker rm -f app-" + repo + "-" + build.ID}, } if err := startUnit("app-"+repo, buildid, unitSlice); err != nil { output.WriteError("Fleet unit start failed: " + err.Error()) output.WriteData("Please verify that fleet is online.") return err } return }
func buildImage(c *workflow.Context) (err error) { dir, ok := c.Arguments["tempdir"] if !ok { return errors.New("Impossible state") } // Build docker image image = config.RegistryHost + ":" + config.RegistryPort + "/" + os.Getenv("USER") + "/" + repo + ":" + sha[:7] // 192.168.45.117:5000/xena/mpd:1fc8018 output.WriteHeader("Building docker image " + image) cmd := exec.Command("docker", "build", "-t", image, dir) cmd.Dir = dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stdout err = cmd.Run() if err != nil { output.WriteError(err.Error()) return err } return }
func injectLayers(c *workflow.Context) (err error) { dir, ok := c.Arguments["tempdir"] if !ok { return errors.New("Impossible state") } // Inject some vars output.WriteHeader("Injecting flitter layers to Dockerfile") dockerfout, err := os.OpenFile(dir+"/Dockerfile", os.O_APPEND|os.O_WRONLY, 0666) if err != nil { output.WriteError("Could not inject things to Dockerfile") return err } _, err = dockerfout.Write([]byte("\nENV GIT_SHA " + sha + "\n")) if err != nil { output.WriteError("Error: " + err.Error()) return err } dockerfout.Write([]byte("ENV BUILTBY " + os.Getenv("USER") + "\n")) dockerfout.Write([]byte("ENV APPNAME " + os.Getenv("REPO") + "\n")) dockerfout.Write([]byte("ENV BUILDID " + buildid)) dockerfout.Close() output.WriteData("done") return }
func successMessage(c *workflow.Context) (err error) { // Print end message output.WriteHeader("Success") output.WriteData("Your app is in the docker registry as " + image) output.WriteData("Your build id is " + buildid) output.WriteData("") output.WriteData("You may access your app at http://" + repo + "." + config.Domain + " once it spins up") return }
// main is the entry point for cloudchaser, the build sentry. func main() { flag.Parse() app := os.Getenv("REPO") user := os.Getenv("USER") client := etcd.NewClient([]string{*etcdmachine}) output.WriteHeader("Checking permission") output.WriteData("user: "******"app: " + app) var allowedusers []string path := constants.ETCD_APPS + app + "/users" res, err := client.Get(path, false, false) if err != nil { output.WriteError("Permissions check failed: " + err.Error()) output.WriteData("Do you have permission to deploy this app?") os.Exit(1) } rawusers := res.Node.Value err = json.Unmarshal([]byte(rawusers), &allowedusers) if err != nil { output.WriteError("Internal json decoding reply in allowed app users parsing") output.WriteData(err.Error()) return } for _, username := range allowedusers { if strings.ToLower(username) == strings.ToLower(user) { goto allowed } } output.WriteError("User is not authorized to make builds") output.WriteData("I think you are " + user) output.WriteData("Please check the needed permissions and try again later.") allowed: output.WriteData("") output.WriteData("Kicking off build") output.WriteData("") os.Exit(0) }
func validateDockerfile(c *workflow.Context) (err error) { dir, ok := c.Arguments["tempdir"] if !ok { return errors.New("Impossible state") } // Validate Docker image output.WriteHeader("Validating Dockerfile") fin, err := os.Open(dir + "/Dockerfile") if err != nil { output.WriteError("Could not validate Dockerfile") return err } exposed := false scanner := bufio.NewReader(fin) line, isPrefix, err := scanner.ReadLine() for err == nil && !isPrefix { s := string(line) split := strings.Split(s, " ") if len(split) == 0 { continue } if strings.ToUpper(split[0]) == "EXPOSE" { if exposed { output.WriteData("Multiple ports exposed") output.WriteData("Please make sure to only expose one port") output.WriteData("You can and will run into undefined behavior") output.WriteData("You have been warned") output.WriteData("") break } else { exposed = true } } line, isPrefix, err = scanner.ReadLine() } fin.Close() output.WriteData("done") return }
// main is the entrypoint for the builder func main() { flag.Parse() if len(flag.Args()) < 3 { *help = true } if *help { fmt.Printf("Usage:\n") fmt.Printf(" builder [options] <repo> <branch> <sha>\n\n") flag.Usage() os.Exit(128) } config = NewConfig(*etcdhost) user = os.Getenv("USER") repo = flag.Arg(0) branch = flag.Arg(1) sha = flag.Arg(2) buildid = sha[0:8] output.WriteHeader("Building " + repo + " branch " + branch + " as " + user) c := workflow.New("builder") c.Use( makeTempDir, extractTarball, checkDockerfile, injectLayers, validateDockerfile, buildImage, tagAndPushImage, deployImage, successMessage, ) err := c.Run() if err != nil { output.WriteError(err.Error()) log.Fatal(err) } }
func extractTarball(c *workflow.Context) (err error) { // Extract branch to deploy output.WriteHeader("Extracting " + repo) dir, ok := c.Arguments["tempdir"] if !ok { return errors.New("Impossible state") } cmd := exec.Command("git", "archive", branch) fout, err := os.Create(dir + "/app.tar") if err != nil { return err } cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { output.WriteHeader("Error in capturing tarball: " + err.Error()) stderr, err := cmd.StderrPipe() if err != nil { output.WriteData("Cannot get debug information") return err } spew := bufio.NewReader(stderr) for { line, _, err := spew.ReadLine() if err == io.EOF { return err } if err != nil { output.WriteData(err) return err } output.WriteData(string(line)) } } _, err = fout.Write(out) if err != nil { output.WriteHeader("Error in writing tarball: " + err.Error()) return err } fout.Sync() fout.Close() output.WriteData("done") // Extract tarball cmd = exec.Command("tar", "xf", "app.tar") cmd.Dir = dir err = cmd.Run() if err != nil { output.WriteHeader("Error in extracting tarball: " + err.Error()) return err } os.Remove(dir + "/app.tar") return nil }