Пример #1
0
// Run Parity - the main application entrypoint
func (p *Parity) Run() {
	log.Banner(banner)

	log.Debug("Loading plugins...")
	p.LoadPlugins()

	// Execute all plugins in parallel?
	// TODO: Initial Sync may need to be blocking so that Run
	//       can work?
	for _, pl := range p.SyncPlugins {
		p.runAsync(pl.Sync)
	}

	// Run all Runners
	for _, pl := range p.RunPlugins {
		p.runAsync(pl.Run)
	}

	// Interrupt handler
	sigChan := make(chan os.Signal, 1)
	p.errorChan = make(chan error)
	signal.Notify(sigChan, os.Interrupt, os.Kill)

	select {
	case e := <-p.errorChan:
		log.Error(e.Error())
	case <-sigChan:
		log.Debug("Received interrupt, shutting down.")
		p.Teardown()
	}
}
Пример #2
0
// XServerProxy creates a TCP proxy on port 6000 to a the Unix
// socket that XQuartz is listening on.
//
// NOTE: this function does not start/install the XQuartz service
func XServerProxy(port int) {
	if runtime.GOOS != "darwin" {
		log.Debug("Not running an OSX environment, skip run X Server Proxy")
		return
	}

	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		log.Fatal(err)
	}
	defer l.Close()

	// Send all traffic back to unix $DISPLAY socket on a running XQuartz server
	addr, err := net.ResolveUnixAddr("unix", os.Getenv("DISPLAY"))
	if err != nil {
		log.Error("Error: ", err.Error())
	}
	log.Info("X Service Proxy available on all network interfaces on port %d", port)
	if host, err := utils.DockerVMHost(); err == nil {
		log.Info("Parity has detected your Docker environment and recommends running 'export DISPLAY=%s:0' in your container to forward the X display", host)
	}

	for {
		xServerClient, err := net.DialUnix("unix", nil, addr)
		if err != nil {
			log.Error("Error: ", err.Error())
		}
		defer xServerClient.Close()

		conn, err := l.Accept()
		log.Debug("X Service Proxy connected to client on: %s (remote: %s)", conn.LocalAddr(), conn.RemoteAddr())
		if err != nil {
			log.Fatal(err)
		}
		go func(c net.Conn, s *net.UnixConn) {
			buf := make([]byte, 8092)
			io.CopyBuffer(s, c, buf)
			s.CloseWrite()
		}(conn, xServerClient)

		go func(c net.Conn, s *net.UnixConn) {
			buf := make([]byte, 8092)
			io.CopyBuffer(c, s, buf)
			c.Close()
		}(conn, xServerClient)
	}
}
Пример #3
0
// 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
}
Пример #4
0
func (e *Excludes) Set(value string) error {
	r, err := regexp.CompilePOSIX(value)
	if err == nil {
		*e = append(*e, *r)
	} else {
		log.Error("Error:", err.Error())
	}

	return nil
}
Пример #5
0
// GetProject returns the Docker project from the configuration
func (c *DockerCompose) GetProject() (p *project.Project, err error) {
	if _, err = os.Stat(c.ComposeFile); err == nil {
		p, err = docker.NewProject(&docker.Context{
			Context: project.Context{
				ComposeFiles: []string{c.ComposeFile},
				ProjectName:  fmt.Sprintf("parity-%s", c.pluginConfig.ProjectNameSafe),
			},
		})

		if err != nil {
			log.Error("Could not create Compose project %s", err.Error())
			return p, err
		}
	} else {
		log.Error("Could not parse compose file: %s", err.Error())
		return p, err
	}

	return p, nil
}
Пример #6
0
// 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
}
Пример #7
0
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
}
Пример #8
0
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
}
Пример #9
0
// 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")
}
Пример #10
0
// 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
}