예제 #1
0
// ProcessHooks is intended for executing appropriate hooks.
// The hooks must contain the following prefix : [0-9]+_
// If arguments are being passed to the function, they will be
// passed tp the hooks as well
// Example:
// 01_pre_deploy
// 02_deploy
// 03_post_deploy
// 04_clean
func ProcessHooks(pathToHooksDir string, hookArgs ...string) error {
	d, err := os.Stat(pathToHooksDir)
	if err != nil {
		return utils.FormatError(err)
	}
	if !d.IsDir() {
		return utils.FormatError(fmt.Errorf("%s is not directory", d.Name()))
	}

	var scriptsSlice []string
	err = filepath.Walk(pathToHooksDir, func(scriptName string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			if found, _ := regexp.MatchString("[0-9]+_", scriptName); found {
				scriptsSlice = append(scriptsSlice, scriptName)
			}
		}
		return nil
	})
	sort.Strings(scriptsSlice)
	for _, file := range scriptsSlice {
		if err := exec.Command(file, hookArgs[0:]...).Run(); err != nil {
			return utils.FormatError(err)
		}
	}
	return nil
}
예제 #2
0
func (b *MetadataBuilder) Run() (deployer.Artifact, error) {
	// in case no source template exists apparently we should use the default metadata
	_, err := os.Stat(b.Source)
	if err != nil {
		b.Source = b.Dest
	}

	f, err := ioutil.ReadFile(b.Source)
	if err != nil {
		return nil, utils.FormatError(err)
	}

	data, err := utils.ProcessTemplate(string(f), b.UserData)
	if err != nil {
		return nil, utils.FormatError(err)
	}

	run := utils.RunFunc(b.SshConfig)
	if _, err := run(fmt.Sprintf("echo \"%s\" > %s", data, b.Dest)); err != nil {
		return nil, utils.FormatError(err)
	}
	return &deployer.CommonArtifact{
		Name: filepath.Base(b.Dest),
		Path: b.Dest,
		Type: deployer.MetadataArtifact,
	}, nil
}
예제 #3
0
// ImageCustomize treating image customization according to XML config files
// Returns error or nil
func Customize(pathToSlash, pathToConfigDir string) error {
	//install/deinstall appropriate packages
	pathToXml := pathToConfigDir + "/packages.xml"
	if _, err := os.Stat(pathToXml); err == nil {
		if err := packageManip(pathToXml, pathToSlash); err != nil {
			return utils.FormatError(err)
		}
	}
	// inject appropriate stuff
	pathToXml = pathToConfigDir + "/inject_items.xml"
	if _, err := os.Stat(pathToXml); err == nil {
		if err := injectManip(pathToXml, pathToSlash); err != nil {
			return utils.FormatError(err)
		}
	}
	// services manipulation
	pathToXml = pathToConfigDir + "/services.xml"
	if _, err := os.Stat(pathToXml); err == nil {
		if err := serviceManip(pathToXml, pathToSlash); err != nil {
			return utils.FormatError(err)
		}
	}
	// file content modification
	pathToXml = pathToConfigDir + "/files_content.xml"
	if _, err := os.Stat(pathToXml); err == nil {
		if err := filesContentManip(pathToXml, pathToSlash); err != nil {
			return utils.FormatError(err)
		}
	}
	return nil
}
예제 #4
0
// Emulator returns appropriate path to QEMU emulator for a given architecture
func (d *Driver) Emulator(arch string) (string, error) {
	switch arch {
	case "x86_64":
	case "i686":
	default:
		return "", utils.FormatError(fmt.Errorf("Unsupported architecture(%s).Only i686 and x86_64 supported", arch))
	}

	out, err := d.Run("virsh capabilities")
	if err != nil {
		return "", utils.FormatError(err)
	}

	m, err := mxj.NewMapXml([]byte(out))
	if err != nil {
		return "", utils.FormatError(err)
	}

	v, _ := m.ValuesForPath("capabilities.guest.arch", "-name:"+arch)
	// fixing https://github.com/dorzheh/deployer/issues/2
	if len(v) == 0 {
		return "", utils.FormatError(fmt.Errorf("Can't gather a KVM guest information for architecture %s.", arch))
	}
	return v[0].(map[string]interface{})["emulator"].(string), nil

}
예제 #5
0
func CreateConfig(d *deployer.CommonData, i *metadata.InputData) (*metadata.Config, error) {
	if d.DefaultExportDir == "" {
		d.DefaultExportDir = "/var/lib/libvirt/images"
	}

	m, err := metadata.NewMetdataConfig(d, i.StorageConfigFile)
	if err != nil {
		return nil, utils.FormatError(err)
	}

	controller.RegisterSteps(func() func() error {
		return func() error {
			var err error
			m.Hwdriver, err = hwinfodriver.NewHostinfoDriver(m.SshConfig, i.Lshw, filepath.Join(d.RootDir, ".hwinfo.json"))
			if err != nil {
				return utils.FormatError(err)
			}

			m.Metadata = new(metadata.Metadata)
			m.EnvDriver = envdriver.NewDriver(m.SshConfig)
			if m.Metadata.EmulatorPath, err = m.EnvDriver.Emulator(d.Arch); err != nil {
				return utils.FormatError(err)
			}
			return controller.SkipStep
		}
	}())

	if err := metadata.RegisterSteps(d, i, m, &meta{}); err != nil {
		return nil, utils.FormatError(err)
	}
	return m, nil
}
예제 #6
0
// Deploy is implementing entire flow
// The flow consists of the following stages:
// - CreateConfig creates appropriate configuration(user interaction against UI).
// - CreateBuilders creates appropriate builders and passes them to the build process
// - CreatePostProcessors creates appropriate post-processors and passes them for post-processing
func Deploy(c *deployer.CommonData, f deployer.FlowCreator) error {
	if err := f.CreateConfig(c); err != nil {
		return utils.FormatError(err)
	}

	builders, err := f.CreateBuilders(c)
	if err != nil {
		return utils.FormatError(err)
	}

	artifacts, err := deployer.BuildProgress(c, builders)
	if err != nil {
		return utils.FormatError(err)
	}

	post, err := f.CreatePostProcessor(c)
	if err != nil {
		return utils.FormatError(err)
	}
	if post != nil {
		err := deployer.PostProcessProgress(c, post, artifacts)
		if err != nil {
			return utils.FormatError(err)
		}
	}
	return nil
}
예제 #7
0
// Build iterates over a slice of builders and runs
// each builder in a separated goroutine.
// Returns a slice of artifacts.
func Build(builders []Builder) ([]Artifact, error) {
	dur, err := time.ParseDuration("1s")
	if err != nil {
		return nil, utils.FormatError(err)
	}

	runtime.GOMAXPROCS(runtime.NumCPU() - 1)

	var artifacts []Artifact
	ch := make(chan *buildResult, len(builders))

	for _, b := range builders {
		time.Sleep(dur)
		go func(b Builder) {
			artifact, err := b.Run()
			// Forwards created artifact to the channel.
			ch <- &buildResult{artifact, err}
		}(b)
	}
	for i := 0; i < len(builders); i++ {
		select {
		case result := <-ch:
			if result.err != nil {
				//defer close(ch)
				return nil, utils.FormatError(result.err)
			}
			artifacts = append(artifacts, result.artifact)
		}
	}
	return artifacts, nil
}
예제 #8
0
func ProcessNetworkTemplate(mode *xmlinput.Mode, defaultTemplate string, tmpltData interface{}, templatesDir string) (string, error) {
	var customTemplate string

	if mode.Tmplt == nil {
		customTemplate = defaultTemplate
	} else {
		var templatePath string
		if templatesDir != "" {
			templatePath = filepath.Join(templatesDir, mode.Tmplt.FileName)
		} else {
			templatePath = filepath.Join(mode.Tmplt.Dir, mode.Tmplt.FileName)
		}

		buf, err := ioutil.ReadFile(templatePath)
		if err != nil {
			return "", utils.FormatError(err)
		}
		customTemplate = string(buf)
	}

	tempData, err := utils.ProcessTemplate(customTemplate, tmpltData)
	if err != nil {
		return "", utils.FormatError(err)
	}
	return string(tempData) + "\n", nil
}
예제 #9
0
func verify(data *XMLInputData) error {
	if data.Networks.Configure {
		for _, net := range data.Networks.Configs {
			seenDirect := false
			seenPassthrough := false
			for _, mode := range net.Modes {
				switch mode.Type {
				case ConTypeDirect:
					seenDirect = true
				case ConTypePassthrough:
					seenPassthrough = true
				case ConTypeBridged, ConTypeOVS, ConTypeVirtualNetwork, ConTypeSRIOV:
				default:
					return utils.FormatError(errors.New("unexpected mode " + string(mode.Type)))
				}
			}
			if seenDirect && seenPassthrough {
				return utils.FormatError(errors.New("either \"direct\" or\"passthrough\" permitted"))
			}
		}
		for _, nic := range data.HostNics.Allowed {
			if nic.Disjunction && nic.Priority {
				return utils.FormatError(errors.New("either \"priority\" or\"disjunction\" permitted"))
			}
		}
	}
	return nil
}
예제 #10
0
// getMappers is responsible for finding mappers bound to appropriate loop device
// and providing the stuff as a slice
func (i *image) getMappers(loopDeviceName string) ([]string, error) {
	var mappers []string
	if _, err := i.run(i.utils.Kpartx + " -a " + loopDeviceName); err != nil {
		return mappers, utils.FormatError(err)
	}
	// somehow on RHEL based systems refresh might take some time therefore
	// no mappers are available until then
	duration, err := time.ParseDuration("1s")
	if err != nil {
		return mappers, utils.FormatError(err)
	}
	time.Sleep(duration)

	cmd := fmt.Sprintf("find /dev -name loop%sp[0-9]",
		strings.TrimSpace(strings.SplitAfter(loopDeviceName, "/dev/loop")[1]))
	out, err := i.run(cmd)
	if err != nil {
		return mappers, utils.FormatError(fmt.Errorf("%s [%v]", out, err))
	}
	for _, line := range strings.Split(out, "\n") {
		if line != "" {
			mappers = append(mappers, line)
		}
	}
	if len(mappers) == 0 {
		return mappers, utils.FormatError(errors.New("mappers not found"))
	}
	sort.Strings(mappers)
	return mappers, nil
}
예제 #11
0
// addMapper registers appropriate mapper and it's mount point
func (i *image) addMapper(mapperDeviceName, path string) error {
	mountPoint := filepath.Join(i.slashpath, path)
	if out, err := i.run("mkdir -p " + mountPoint); err != nil {
		return utils.FormatError(fmt.Errorf("%s [%v]", out, err))
	}
	// check if the volume is already mounted
	mounted, err := isMounted(mapperDeviceName)
	if err != nil {
		return utils.FormatError(err)
	}
	if !mounted {
		if out, err := i.run(fmt.Sprintf("mount %s %s", mapperDeviceName, mountPoint)); err != nil {
			return utils.FormatError(fmt.Errorf("%s [%v]", out, err))
		}
	}
	// add mapper
	i.mappers = append(i.mappers,
		&mapperDevice{
			name:       mapperDeviceName,
			mountPoint: mountPoint,
		},
	)
	// advance amount of mappers
	i.loopDevice.amountOfMappers++
	return nil
}
예제 #12
0
// ParseXMLInputFile is responsible for parsing a given configuration file
// representing appropriate input data to a srtuctured form
func ParseXMLInput(config string) (*XMLInputData, error) {
	d, err := utils.ParseXMLFile(config, new(XMLInputData))
	if err != nil {
		return nil, utils.FormatError(err)
	}
	if err := verify(d.(*XMLInputData)); err != nil {
		return nil, utils.FormatError(err)
	}
	return d.(*XMLInputData), nil
}
예제 #13
0
// ParseXMLInputBuf is responsible for parsing a given stream of bytes
// representing appropriate input data to a srtuctured form
func ParseXMLInputBuf(data []byte) (*XMLInputData, error) {
	d, err := utils.ParseXMLBuff(data, new(XMLInputData))
	if err != nil {
		return nil, utils.FormatError(err)
	}
	if err := verify(d.(*XMLInputData)); err != nil {
		return nil, utils.FormatError(err)
	}
	return d.(*XMLInputData), nil
}
예제 #14
0
func NewParserBuff(bundleConfigStream []byte, s BundleStrategy) (*Parser, error) {
	if s == nil {
		return nil, utils.FormatError(errors.New("bundle strategy is nil"))
	}
	data, err := utils.ParseXMLBuff(bundleConfigStream, s)
	if err != nil {
		return nil, utils.FormatError(err)
	}
	return &Parser{s, data}, nil
}
예제 #15
0
func NewParser(bundleConfigFile string, s BundleStrategy) (*Parser, error) {
	if s == nil {
		return nil, utils.FormatError(errors.New("bundle strategy is nil"))
	}

	data, err := utils.ParseXMLFile(bundleConfigFile, s)
	if err != nil {
		return nil, utils.FormatError(err)
	}
	return &Parser{s, data}, nil
}
예제 #16
0
// Parse parses lshw output
func (c *Collector) Hwinfo2Json() error {
	lshwNewPath, err := c.prepare()
	if err != nil {
		return utils.FormatError(err)
	}
	out, err := c.Run(lshwNewPath + " -class network -class cpu -json")
	if err != nil {
		return utils.FormatError(err)
	}
	return ioutil.WriteFile(c.hwinfoFile, []byte(out), 0)
}
예제 #17
0
func (b *InstanceBuilder) Run() (a deployer.Artifact, err error) {
	if err = b.Filler.CustomizeRootfs("/"); err != nil {
		err = utils.FormatError(err)
		return
	}
	if err = b.Filler.InstallApp("/"); err != nil {
		err = utils.FormatError(err)
		return
	}
	return
}
예제 #18
0
func (c *Collector) CPUs() (int, error) {
	cpustr, err := c.Run(`grep -c ^processor /proc/cpuinfo`)
	if err != nil {
		return 0, utils.FormatError(err)
	}

	cpus, err := strconv.Atoi(strings.Trim(cpustr, "\n"))
	if err != nil {
		return 0, utils.FormatError(err)
	}
	return int(cpus), nil
}
예제 #19
0
// filesContentManip manipulates with the content of the files
// according to appropriate XML configuration file
// Example:
//<files>
//	<file>
//		<path>/etc/sysconfig/selinux</path>
//		<bkp_name>/etc/sysconfig/selinux.bkp</bkp_name>
//		<action>replace</action>
//		<old_pattern>SELINUX=\S+</old_pattern>
//		<new_pattern>SELINUX=disabled</new_pattern>
//	</file>
//	<file>
//		<path>/etc/passwd</path>
//		<bkp_name>/etc/passwd.bak</bkp_name>
//		<action>append</action>
//		<old_pattern></old_pattern>
//		<new_pattern>test:x:111:111::/root:/bin/bash</new_pattern>
//	</file>
//</files>
func filesContentManip(pathToXml, pathToSlash string) error {
	dataBuf, err := ioutil.ReadFile(pathToXml)
	if err != nil {
		return utils.FormatError(err)
	}
	fileContentStruct := FilesContent{}
	if err := xml.Unmarshal(dataBuf, &fileContentStruct); err != nil {
		return utils.FormatError(err)
	}
	for _, val := range fileContentStruct.FContent {
		targetPath := filepath.Join(pathToSlash, val.Path)
		if err != nil {
			return utils.FormatError(err)
		}
		finfo, err := os.Stat(targetPath)
		if err != nil {
			continue
		}
		if val.NewPattern == "" {
			return utils.FormatError(errors.New("configuration error - NewPattern is empty"))
		}
		if val.BkpName != "" {
			bkpFilePath := filepath.Join(pathToSlash, val.BkpName)
			if err := ioutils.CopyFile(targetPath, bkpFilePath, 0, -1, -1, false); err != nil {
				return utils.FormatError(err)
			}
		}
		fd, err := os.OpenFile(targetPath, os.O_RDWR|os.O_APPEND, finfo.Mode())
		if err != nil {
			return utils.FormatError(err)
		}
		defer fd.Close()

		switch val.Action {
		// if we need to append to the file
		case ACTION_APPEND:
			if err := ioutils.AppendToFd(fd, val.NewPattern+"\n", val.NewPattern); err != nil {
				return utils.FormatError(err)
			}
		// if we need to replace a pattern
		case ACTION_REPLACE:
			if val.OldPattern == "" {
				return utils.FormatError(errors.New("configuration error - replace action is set but OldPattern is empty"))
			}
			if err := ioutils.FindAndReplaceFd(fd, val.OldPattern, val.NewPattern); err != nil {
				return utils.FormatError(err)
			}
		default:
			return utils.FormatError(errors.New(`FilesContentManip:configuration error - unsupported action`))
		}
	}
	return nil
}
예제 #20
0
func (i *image) bind(imagePath string) (loopDevice string, err error) {
	loopDevice, err = i.run("losetup -f")
	if err != nil {
		err = utils.FormatError(err)
		return
	}
	cmd := fmt.Sprintf("losetup %s %s", loopDevice, imagePath)
	if out, er := i.run(cmd); err != nil {
		err = utils.FormatError(fmt.Errorf("%s [%v]", out, er))
		return
	}
	return
}
예제 #21
0
// convert is responsible for converting RAW image to other format
func (i *image) convert() error {
	// set the new path - append extention
	newPath := fmt.Sprintf("%s.%s", strings.TrimSuffix(i.config.Path, ".raw"), i.config.Type)
	if out, err := i.run(fmt.Sprintf("qemu-img convert -f raw -O %s %s %s", i.config.Type, i.config.Path, newPath)); err != nil {
		return utils.FormatError(fmt.Errorf("%s [%v]", out, err))
	}
	//remove temporary image
	if out, err := i.run("rm -rf " + i.config.Path); err != nil {
		return utils.FormatError(fmt.Errorf("%s [%v]", out, err))
	}
	// expose the new path
	i.config.Path = newPath
	return nil
}
예제 #22
0
func (b *ImageBuilder) Run() (deployer.Artifact, error) {
	if err := os.MkdirAll(b.RootfsMp, 0755); err != nil {
		return nil, utils.FormatError(err)
	}

	defer os.RemoveAll(b.RootfsMp)

	// create new image artifact
	img, err := image.New(b.ImageConfig, b.RootfsMp, b.Utils, b.SshfsConfig)
	if err != nil {
		return nil, utils.FormatError(err)
	}
	// interrupt handler
	img.ReleaseOnInterrupt()
	defer func() {
		img.Cleanup()
	}()

	// parse the image
	if err := img.Parse(); err != nil {
		return nil, utils.FormatError(err)
	}
	// customize rootfs
	if b.Filler != nil {
		if err := b.Filler.CustomizeRootfs(b.RootfsMp); err != nil {
			return nil, utils.FormatError(err)
		}
		// install application.
		if err := b.Filler.InstallApp(b.RootfsMp); err != nil {
			return nil, utils.FormatError(err)
		}
	}
	if b.ImageConfig.Bootable {
		if err := img.MakeBootable(); err != nil {
			return nil, utils.FormatError(err)
		}
	}
	if b.Filler != nil {
		if err := b.Filler.RunHooks(b.RootfsMp); err != nil {
			return nil, utils.FormatError(err)
		}
	}
	if err := img.Cleanup(); err != nil {
		return nil, utils.FormatError(err)
	}
	if err := img.Convert(); err != nil {
		return nil, utils.FormatError(err)
	}
	return &deployer.CommonArtifact{
		Name: filepath.Base(b.ImageConfig.Path),
		Path: b.ImageConfig.Path,
		Type: deployer.ImageArtifact,
	}, nil
}
예제 #23
0
func (b *DirBuilder) Run() (a deployer.Artifact, err error) {
	// customize rootfs
	if b.Filler != nil {
		if err = b.Filler.CustomizeRootfs(b.RootfsPath); err != nil {
			err = utils.FormatError(err)
			return
		}
		// install application
		if err = b.Filler.InstallApp(b.RootfsPath); err != nil {
			err = utils.FormatError(err)
			return
		}
	}
	return
}
예제 #24
0
// numa4Nic fetchs NUMA node for appropriate PCI device
func (c *Collector) numa4Nic(pciAddr string) (int, error) {
	numaInt := 0
	numaStr, err := c.Run("cat /sys/bus/pci/devices/" + pciAddr + "/numa_node")
	if err != nil {
		return numaInt, utils.FormatError(err)
	}
	if numaStr == "-1" {
		numaInt = 0
	} else {
		numaInt, err = strconv.Atoi(numaStr)
		if err != nil {
			return numaInt, utils.FormatError(err)
		}
	}
	return numaInt, nil
}
예제 #25
0
파일: ui.go 프로젝트: weldpua2008/deployer
func UiSelectEnv(c *deployer.CommonData, envList []string, envs []deployer.FlowCreator) error {
	var menuList []string
	indexInt := 1

	for _, env := range envList {
		menuList = append(menuList, strconv.Itoa(indexInt), env)
		indexInt++
	}

	envsNum := len(envs)
	dType := 0
	if envsNum > 1 {
		c.Ui.SetTitle("Select environment")
		c.Ui.SetSize(envsNum+7, 30)
		resStr, err := c.Ui.Menu(envsNum, menuList[0:]...)
		if err != nil && err.Error() == gui.DialogExit {
			os.Exit(0)
		}
		dType, err = strconv.Atoi(resStr)
		if err != nil {
			return utils.FormatError(err)
		}
		dType--
	}
	return main.Deploy(c, envs[dType])
}
예제 #26
0
// Destroy is responsible for removing appropriate artifact.
func (a *CommonArtifact) Destroy() error {
	run := utils.RunFunc(a.SshConfig)
	if _, err := run("rm " + a.Path); err != nil {
		return utils.FormatError(err)
	}
	return nil
}
예제 #27
0
// ParseConfigFile is responsible for reading appropriate XML file
// and calling ParseConfig for further processing
func ParseConfigFile(xmlpath string) (*Storage, error) {
	fb, err := ioutil.ReadFile(xmlpath)
	if err != nil {
		return nil, utils.FormatError(err)
	}
	return ParseConfig(fb)
}
예제 #28
0
// DiskConfig returns disk configuration for appropriate index
func (s *Storage) IndexToConfig(index ConfigIndex) (*Config, error) {
	t := s.Configs[index]
	if t == nil {
		return nil, utils.FormatError(fmt.Errorf("no configuration found for index %d", index))
	}
	return t, nil
}
예제 #29
0
// Parse processes RAW image
// Returns error/nil
func (i *image) Parse() error {
	var err error
	if i.loopDevice.name, err = i.bind(i.config.Path); err != nil {
		return utils.FormatError(err)
	}
	if i.needToFormat {
		if err := i.partTableMakefs(); err != nil {
			return utils.FormatError(err)
		}
	} else if i.config.Partitions != nil {
		if err := i.addMappers(); err != nil {
			return utils.FormatError(err)
		}
	}
	return nil
}
예제 #30
0
// create is intended for creating RAW image
func (i *image) create() error {
	out, err := i.run(fmt.Sprintf("dd if=/dev/zero of=%s count=1 bs=1 seek=%vM", i.config.Path, i.config.SizeMb))
	if err != nil {
		return utils.FormatError(fmt.Errorf("%s [%v]", out, err))
	}
	return nil
}