// Shell creates an interactive Docker session to the specified service // starting it if not currently running func (c *DockerCompose) Shell(config parity.ShellConfig) (err error) { log.Stage("Interactive Shell") log.Debug("Compose - starting docker compose services") mergedConfig := *parity.DEFAULT_INTERACTIVE_SHELL_OPTIONS mergo.MergeWithOverwrite(&mergedConfig, &config) // Check if services running // TODO: if running, attach to running container // if NOT running, start services and attach if c.project != nil { log.Step("Starting compose services") injectDisplayEnvironmentVariables(c.project) } container := fmt.Sprintf("parity-%s_%s_1", c.pluginConfig.ProjectNameSafe, mergedConfig.Service) client := utils.DockerClient() createExecOptions := dockerclient.CreateExecOptions{ AttachStdin: true, AttachStdout: true, AttachStderr: true, Tty: true, Cmd: mergedConfig.Command, User: mergedConfig.User, Container: container, } startExecOptions := dockerclient.StartExecOptions{ Detach: false, Tty: true, InputStream: os.Stdin, OutputStream: os.Stdout, ErrorStream: os.Stderr, RawTerminal: true, } log.Step("Attaching to container '%s'", container) if id, err := client.CreateExec(createExecOptions); err == nil { client.StartExec(id.ID, startExecOptions) } else { log.Error("error: %v", err.Error()) } log.Debug("Docker Compose Run() finished") return err }
// Run the Docker Compose Run Plugin // // Detects docker-compose.yml files, builds and runs. func (c *DockerCompose) Run() (err error) { log.Stage("Run Docker") log.Step("Building base image") c.Build() log.Step("Building compose project") if c.project != nil { log.Debug("Compose - starting docker compose services") go c.runXServerProxy() c.project.Delete() c.project.Build() c.project.Up() } log.Debug("Docker Compose Run() finished") return err }
// Init creates a default parity.yml file in the current dir func Init() { log.Stage("Initialising Parity") log.Step("Creating 'parity.yml'") type FileTemplate struct { Name string } templateData := FileTemplate{Name: "somefile"} // Create the install mirror daemon template file := utils.CreateTemplateTempFile(templatesParityYmlBytes, 0655, templateData) os.Rename(file.Name(), "parity.yml") log.Stage("Initialising Parity : Complete") }
// Attach attaches to the specified service in the running container func (c *DockerCompose) Attach(config parity.ShellConfig) (err error) { log.Stage("Interactive Shell") log.Debug("Compose - starting docker compose services") mergedConfig := *parity.DEFAULT_INTERACTIVE_SHELL_OPTIONS mergo.MergeWithOverwrite(&mergedConfig, &config) client := utils.DockerClient() container := fmt.Sprintf("parity-%s_%s_1", c.pluginConfig.ProjectNameSafe, mergedConfig.Service) opts := dockerclient.AttachToContainerOptions{ Stdin: true, Stdout: true, Stderr: true, InputStream: os.Stdin, OutputStream: os.Stdout, ErrorStream: os.Stderr, RawTerminal: true, Container: container, Stream: true, Logs: true, } if c.project.Configs[config.Service] == nil { return fmt.Errorf("Service %s does not exist", config.Service) } log.Step("Attaching to container '%s'", container) if err := client.AttachToContainer(opts); err == nil { err = c.project.Up(config.Service) if err != nil { log.Error("error: %s", err.Error()) return err } } else { return err } _, err = client.WaitContainer(container) log.Error("wc error: %s", err.Error()) log.Debug("Docker Compose Run() finished") return err }
func (p *Mirror) Sync() error { log.Stage("Synchronising source/dest folders") pkiMgr, err := pki.New() pkiMgr.Config.Insecure = true if err != nil { p.pluginConfig.Ui.Error(fmt.Sprintf("Unable to setup public key infrastructure: %s", err.Error())) } Config, err := pkiMgr.GetClientTLSConfig() if err != nil { p.pluginConfig.Ui.Error(fmt.Sprintf("%v", err)) } // Removing shared folders if utils.CheckSharedFolders() { utils.UnmountSharedFolders() } // Read volumes for share/watching var volumes []string // Exclude non-local volumes (e.g. might want to mount a dir on the VM guest) for _, v := range utils.ReadComposeVolumes() { if _, err := os.Stat(v); err == nil { volumes = append(volumes, v) } } // Add PWD if nothing in compose dir, _ := os.Getwd() if len(volumes) == 0 { volumes = append(volumes, mutils.LinuxPath(dir)) } pki.MirrorConfig.ClientTlsConfig = Config excludes := make([]regexp.Regexp, len(p.Exclude)) for i, v := range p.Exclude { r, err := regexp.CompilePOSIX(v) if err == nil { excludes[i] = *r } else { log.Error("Error parsing Regex:", err.Error()) } } options := &sync.Options{Exclude: excludes, Verbose: p.Verbose} // Sync and watch all volumes for _, v := range volumes { log.Step("Syncing contents of '%s' -> '%s'", v, fmt.Sprintf("mirror://%s%s", utils.MirrorHost(), v)) err = sync.Sync(v, fmt.Sprintf("mirror://%s%s", utils.MirrorHost(), v), options) if err != nil { log.Error("Error during initial file sync: %v", err) } log.Step("Monitoring '%s' for changes", v) go sync.Watch(v, fmt.Sprintf("mirror://%s%s", utils.MirrorHost(), v), options) } sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, os.Kill) <-sigChan log.Debug("Interrupt received, shutting down") return nil }
func SetupParityProject(config *Config) error { log.Stage("Setup Parity Project") // 1. Merge Config with Defaults -> need to create Base, Ci and Production image names if err := expandAndValidateConfig(config); err != nil { return err } var parityTemplate []string // Scan template index file if config.templateIndex != "" { log.Step("Downloading template index: %s", config.templateIndex) resp, err := http.Get(config.templateIndex) if err != nil { return err } scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { parityTemplate = append(parityTemplate, scanner.Text()) } } // Make .parity dir if not exists if _, err := os.Stat(".parity"); err == nil { os.Mkdir(".parity", 0755) } errors := make(chan error) done := make(chan bool) defer close(done) // Download and install templates in parallel go func() { wg := sync.WaitGroup{} wg.Add(len(parityTemplate)) dir, _ := os.Getwd() for _, f := range parityTemplate { go func(f string, errorChan chan error) { // 1. Check presence of local File - overwrite? TODO: config targetFile := filepath.Join(dir, f) if _, err := os.Stat(targetFile); err == nil { if !config.Overwrite { errorChan <- fmt.Errorf("File '%s' already exists. Please specify --force to overwrite files.", targetFile) wg.Done() return } } // 2. Download resources // 2a. TODO: Check/store local cache? // 2b. Pull from remote url := fmt.Sprintf(`%s/%s`, config.TemplateSourceURL, f) log.Step("Downloading template file: %s", url) resp, err := http.Get(url) var file *os.File if err == nil { // 3. Interpolate template with Setup data if file, err = tempFile(resp.Body, config); err != nil { errorChan <- err wg.Done() return } } else { log.Error("Error downloading template: %s", err.Error()) errorChan <- fmt.Errorf("Error downloading template: %s", err.Error()) wg.Done() return } // 4. Move to local folder. log.Debug("Moving %s -> %s", file.Name(), targetFile) log.Debug("Ensuring parent dir exists: %s", filepath.Dir(targetFile)) os.MkdirAll(filepath.Dir(targetFile), 0755) os.Rename(file.Name(), targetFile) wg.Done() }(f, errors) } wg.Wait() done <- true }() select { case e := <-errors: log.Error(e.Error()) return e case <-done: log.Debug("Finished installing template") } log.Stage("Setup Parity Project : Complete") return nil }
// InstallParity installs Parity into the running Docker Machine func InstallParity(config InstallConfig) { log.Stage("Install Parity") if config.DevHost == "" { config.DevHost = "parity.local" } // Create DNS entry if config.Dns { hostname := strings.Split(utils.DockerHost(), ":")[0] log.Step("Creating host entry: %s -> %s", hostname, config.DevHost) var hosts goodhosts.Hosts var err error if hosts, err = goodhosts.NewHosts(); err == nil { hosts.Add(hostname, config.DevHost) } else { log.Error("Unable to create DNS Entry: %s", err.Error()) } if err = hosts.Flush(); err != nil { log.Error("Unable to create DNS Entry: %s", err.Error()) } } // Check - is there a Docker Machine created? // -> If so, use the currently selected machine // -> If not, create another machine // -> Persist these settings in ~/.parityrc? // Wrap the local Docker command so that we don't have to use Docker Machine all of the time! type FileTemplate struct { Version string } templateData := FileTemplate{Version: version.Version} // Create the install mirror daemon template file := utils.CreateTemplateTempFile(templatesBootlocalShBytes, 0655, templateData) session, err := utils.SSHSession(utils.DockerHost()) if err != nil { log.Fatalf("Unable to connect to Docker utils.DockerHost(). Is Docker running? (%v)", err.Error()) } log.Step("Installing bootlocal.sh on Docker Host") remoteTmpFile := fmt.Sprintf("/tmp/%s", filepath.Base(file.Name())) err = scp.CopyPath(file.Name(), remoteTmpFile, session) utils.RunCommandWithDefaults(utils.DockerHost(), fmt.Sprintf("sudo cp %s %s", remoteTmpFile, "/var/lib/boot2docker/bootlocal.sh")) session.Close() file = utils.CreateTemplateTempFile(templatesMirrorDaemonShBytes, 0655, templateData) session, err = utils.SSHSession(utils.DockerHost()) if err != nil { log.Fatalf("Unable to connect to Docker utils.DockerHost(). Is Docker running? (%v)", err.Error()) } log.Step("Installing mirror-daemon.sh on Docker Host") remoteTmpFile = fmt.Sprintf("/tmp/%s", filepath.Base(file.Name())) err = scp.CopyPath(file.Name(), remoteTmpFile, session) utils.RunCommandWithDefaults(utils.DockerHost(), fmt.Sprintf("sudo cp %s %s", remoteTmpFile, "/var/lib/boot2docker/mirror-daemon.sh")) session.Close() log.Step("Downloading file sync utility (mirror)") utils.RunCommandWithDefaults(utils.DockerHost(), fmt.Sprintf("sudo chmod +x /var/lib/boot2docker/*.sh")) utils.RunCommandWithDefaults(utils.DockerHost(), fmt.Sprintf(" /var/lib/boot2docker/*.sh")) utils.RunCommandWithDefaults(utils.DockerHost(), fmt.Sprintf("sudo /var/lib/boot2docker/bootlocal.sh start")) log.Step("Restarting Docker") utils.RunCommandWithDefaults(utils.DockerHost(), "sudo shutdown -r now") utils.WaitForNetwork("docker", utils.DockerHost()) utils.WaitForNetwork("mirror", utils.MirrorHost()) // Removing shared folders if utils.CheckSharedFolders() { log.Step("Unmounting Virtualbox shared folders") utils.UnmountSharedFolders() } log.Stage("Install Parity : Complete") }
// Build will build all images in the Parity setup func (c *DockerCompose) Build() error { log.Stage("Bulding containers") base := "Dockerfile" cwd, _ := os.Getwd() baseVersion := c.generateContainerVersion(cwd, base) imageName := fmt.Sprintf("%s:%s", c.ImageName, baseVersion) client, _ := dockerclient2.NewEnvClient() log.Step("Checking if image %s exists locally", imageName) if images, err := client.ImageList(context.Background(), types.ImageListOptions{MatchName: imageName}); err == nil { for _, i := range images { log.Info("Found image: %s", i.ID) return nil } } log.Step("Image %s not found locally, pulling", imageName) client.ImagePull(context.Background(), types.ImagePullOptions{ImageID: imageName}, nil) log.Step("Image %s not found anywhere, building", imageName) ctx, err := c.CreateTar(".", "Dockerfile") if err != nil { return err } defer ctx.Close() var progBuff io.Writer = os.Stdout var buildBuff io.Writer = os.Stdout // Setup an upload progress bar progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true) var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon") logrus.Infof("Building %s...", imageName) outFd, isTerminalOut := term.GetFdInfo(os.Stdout) // Publish latest and specific version response, err := client.ImageBuild(context.Background(), types.ImageBuildOptions{ Context: body, Tags: []string{imageName, fmt.Sprintf("%s:latest", c.ImageName)}, NoCache: false, Remove: true, Dockerfile: "Dockerfile", }) if err != nil { log.Error(err.Error()) return err } err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, outFd, isTerminalOut, nil) if err != nil { if jerr, ok := err.(*jsonmessage.JSONError); ok { // If no error code is set, default to 1 if jerr.Code == 0 { jerr.Code = 1 } fmt.Fprintf(os.Stderr, "%s%s", progBuff, buildBuff) return fmt.Errorf("Status: %s, Code: %d", jerr.Message, jerr.Code) } } return err }