Пример #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
// 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
}
Пример #3
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)
	}
}
Пример #4
0
// Teardown stops any running projects before Parity exits
func (c *DockerCompose) Teardown() error {
	log.Debug("Tearing down 'Docker Machine' 'Run' plugin")

	if c.project != nil {
		c.project.Down()
	}
	return nil
}
Пример #5
0
// Configure sets up this plugin with initial state
func (c *DockerCompose) Configure(pc *parity.PluginConfig) {
	log.Debug("Configuring 'Docker Machine' 'Run\\Build\\Shell' plugin")
	c.pluginConfig = pc
	var err error
	if c.project, err = c.GetProject(); err != nil {
		log.Fatalf("Unable to create Compose Project: %s", err.Error())
	}
}
Пример #6
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
}
Пример #7
0
// Build runs all builders on the project, e.g. Docker build
func (p *Parity) Build() error {
	log.Debug("Loading plugins...")
	p.LoadPlugins()

	for _, pl := range p.BuildPlugins {
		if err := pl.Build(); err != nil {
			return err
		}
	}
	return nil
}
Пример #8
0
// 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
}
Пример #9
0
// generateContainerVersion creates a unique hash for a given Dockerfile.
// It uses the contents of the Dockerfile and any package lock file (package.json, Gemfile etc.)
// Replaces this shell: `echo $(md5Files $(find -L $1 -maxdepth 1 | egrep "(Gemfile.lock|package\.json|Dockerfile)"))`
func (c *DockerCompose) generateContainerVersion(dirName string, dockerfile string) string {
	log.Debug("Looking for %s and related package files in: %s", dockerfile, dirName)
	dir, _ := os.Open(dirName)
	files, _ := dir.Readdir(-1)

	var data []byte
	regex, _ := regexp.CompilePOSIX(fmt.Sprintf("(Gemfile.lock|package\\.json|^%s$)", dockerfile))

	for _, f := range files {
		if regex.MatchString(f.Name()) {
			log.Debug("Found file: %s", f.Name())
			if d, err := ioutil.ReadFile(filepath.Join(dirName, f.Name())); err == nil {
				data = append(data, d...)
			}
		}
	}
	if len(data) == 0 {
		return ""
	}

	return fmt.Sprintf("%x", md5.Sum(data))
}
Пример #10
0
// LoadPlugins loads all plugins referenced in the parity.yml file
// from those registered at runtime
func (p *Parity) LoadPlugins() {
	log.Debug("loading plugins")
	var err error
	var confLoader *plugo.ConfigLoader
	c := &config.RootConfig{}

	if p.config.ConfigFile != "" {
		confLoader = &plugo.ConfigLoader{}
		err = confLoader.LoadFromFile(p.config.ConfigFile, &c)
		if err != nil {
			log.Fatalf("Unable to read configuration file: %s", err.Error())
		}
	} else {
		log.Fatalf("No configuration file provided. Please create a 'parity.yml' file.")
	}
	log.SetLevel(log.LogLevel(c.LogLevel))

	// Load all plugins
	p.pluginConfig = &PluginConfig{Ui: p.config.Ui}

	// Set project name
	p.pluginConfig.ProjectName = c.Name
	p.pluginConfig.ProjectNameSafe = strings.Replace(strings.ToLower(c.Name), " ", "", -1)

	// Sync plugins
	p.SyncPlugins = make([]Sync, len(c.Sync))
	syncPlugins := plugo.LoadPluginsWithConfig(confLoader, c.Sync)

	for i, pl := range syncPlugins {
		log.Debug("Loading Sync Plugin\t" + log.Colorize(log.YELLOW, c.Sync[i].Name))
		p.SyncPlugins[i] = pl.(Sync)
		p.SyncPlugins[i].Configure(p.pluginConfig)
		p.plugins = append(p.plugins, p.SyncPlugins[i])
	}

	// Run plugins
	p.RunPlugins = make([]Run, len(c.Run))
	runPlugins := plugo.LoadPluginsWithConfig(confLoader, c.Run)

	for i, pl := range runPlugins {
		log.Debug("Loading Run Plugin\t" + log.Colorize(log.YELLOW, c.Run[i].Name))
		p.RunPlugins[i] = pl.(Run)
		p.RunPlugins[i].Configure(p.pluginConfig)
		p.plugins = append(p.plugins, p.RunPlugins[i])
	}

	// Build plugins
	p.BuildPlugins = make([]Builder, len(c.Build))
	buildPlugins := plugo.LoadPluginsWithConfig(confLoader, c.Build)

	for i, pl := range buildPlugins {
		log.Debug("Loading Build Plugin\t" + log.Colorize(log.YELLOW, c.Build[i].Name))
		p.BuildPlugins[i] = pl.(Builder)
		p.BuildPlugins[i].Configure(p.pluginConfig)
		p.plugins = append(p.plugins, p.BuildPlugins[i])
	}

	// Shell plugins
	p.ShellPlugins = make([]Shell, len(c.Shell))
	shellPlugins := plugo.LoadPluginsWithConfig(confLoader, c.Shell)

	for i, pl := range shellPlugins {
		log.Debug("Loading Shell Plugin\t" + log.Colorize(log.YELLOW, c.Shell[i].Name))
		p.ShellPlugins[i] = pl.(Shell)
		p.ShellPlugins[i].Configure(p.pluginConfig)
		p.plugins = append(p.plugins, p.ShellPlugins[i])
	}
}
Пример #11
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
}
Пример #12
0
func (m *Mirror) Teardown() error {
	log.Debug("Tearing down mirror sync plugin")
	return nil
}
Пример #13
0
func (m *Mirror) Configure(c *parity.PluginConfig) {
	log.Debug("Configuring mirror sync plugin")
	m.pluginConfig = c
}
Пример #14
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
}