Пример #1
0
func RunInit() error {
	os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin")
	if isInitrd() {
		log.Debug("Booting off an in-memory filesystem")
		// Magic setting to tell Docker to do switch_root and not pivot_root
		os.Setenv("DOCKER_RAMDISK", "true")
	} else {
		log.Debug("Booting off a persistent filesystem")
	}

	initFuncs := []config.CfgFunc{
		func(c *config.CloudConfig) (*config.CloudConfig, error) {
			return c, dockerlaunch.PrepareFs(&mountConfig)
		},
		mountOem,
		func(_ *config.CloudConfig) (*config.CloudConfig, error) {
			cfg, err := config.LoadConfig()
			if err != nil {
				return cfg, err
			}

			if cfg.Rancher.Debug {
				cfgString, err := config.Dump(false, false, true)
				if err != nil {
					log.WithFields(log.Fields{"err": err}).Error("Error serializing config")
				} else {
					log.Debugf("Config: %s", cfgString)
				}
			}

			return cfg, nil
		},
		loadModules,
		tryMountAndBootstrap,
		func(_ *config.CloudConfig) (*config.CloudConfig, error) {
			return config.LoadConfig()
		},
		loadModules,
		sysInit,
	}

	cfg, err := config.ChainCfgFuncs(nil, initFuncs...)
	if err != nil {
		return err
	}

	launchConfig, args := getLaunchConfig(cfg, &cfg.Rancher.SystemDocker)
	launchConfig.Fork = !cfg.Rancher.SystemDocker.Exec

	log.Info("Launching System Docker")
	_, err = dockerlaunch.LaunchDocker(launchConfig, config.DOCKER_BIN, args...)
	if err != nil {
		return err
	}
	return pidOne()
}
Пример #2
0
func enable(c *cli.Context) {
	changed := false
	cfg, err := config.LoadConfig()
	if err != nil {
		logrus.Fatal(err)
	}

	for _, service := range c.Args() {
		if val, ok := cfg.Rancher.ServicesInclude[service]; !ok || !val {
			if strings.HasPrefix(service, "/") && !strings.HasPrefix(service, "/var/lib/rancher/conf") {
				logrus.Fatalf("ERROR: Service should be in path /var/lib/rancher/conf")
			}
			if _, err := compose.LoadServiceResource(service, true, cfg); err != nil {
				logrus.Fatalf("could not load service %s", service)
			}
			cfg.Rancher.ServicesInclude[service] = true
			changed = true
		}
	}

	if changed {
		if err := cfg.Save(); err != nil {
			logrus.Fatal(err)
		}
	}
}
Пример #3
0
func Generate(generateServer bool, outDir string, hostnames []string) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	if outDir == "" {
		return fmt.Errorf("out directory (-d, --dir) not specified")
	}
	caCertPath := filepath.Join(outDir, "ca.pem")
	caKeyPath := filepath.Join(outDir, "ca-key.pem")
	certPath := filepath.Join(outDir, "cert.pem")
	keyPath := filepath.Join(outDir, "key.pem")

	if generateServer {
		certPath = filepath.Join(outDir, "server-cert.pem")
		keyPath = filepath.Join(outDir, "server-key.pem")
	}

	if _, err := os.Stat(outDir); os.IsNotExist(err) {
		if err := os.MkdirAll(outDir, 0700); err != nil {
			return err
		}
	}

	if err := writeCaCerts(cfg, caCertPath, caKeyPath); err != nil {
		return err
	}

	return writeCerts(generateServer, hostnames, cfg, certPath, keyPath, caCertPath, caKeyPath)
}
Пример #4
0
func Main() {
	cfg := config.LoadConfig()

	execID, resp, err := startDocker(cfg)
	if err != nil {
		log.Fatal(err)
	}

	process, err := getDockerProcess()
	if err != nil {
		log.Fatal(err)
	}

	handleTerm(process)

	// Wait for Docker daemon to exit
	io.Copy(ioutil.Discard, resp.Reader)
	resp.Close()

	client, err := rosDocker.NewSystemClient()
	if err != nil {
		log.Fatal(err)
	}

	state, err := client.ContainerExecInspect(context.Background(), execID)
	if err != nil {
		log.Fatal(err)
	}

	// Proxy exit code
	os.Exit(state.ExitCode)
}
Пример #5
0
func enable(c *cli.Context) {
	cfg, err := config.LoadConfig()
	if err != nil {
		logrus.Fatal(err)
	}

	var enabledServices []string

	for _, service := range c.Args() {
		if val, ok := cfg.Rancher.ServicesInclude[service]; !ok || !val {
			if strings.HasPrefix(service, "/") && !strings.HasPrefix(service, "/var/lib/rancher/conf") {
				logrus.Fatalf("ERROR: Service should be in path /var/lib/rancher/conf")
			}

			cfg.Rancher.ServicesInclude[service] = true
			enabledServices = append(enabledServices, service)
		}
	}

	if len(enabledServices) > 0 {
		if err := compose.StageServices(cfg, enabledServices...); err != nil {
			logrus.Fatal(err)
		}

		if err := cfg.Save(); err != nil {
			logrus.Fatal(err)
		}
	}
}
Пример #6
0
Файл: config.go Проект: Jdesk/os
func runImport(c *cli.Context) {
	var input io.ReadCloser
	var err error
	input = os.Stdin
	cfg, err := config.LoadConfig()

	if err != nil {
		log.Fatal(err)
	}

	inputFile := c.String("input")
	if inputFile != "" {
		input, err = os.Open(inputFile)
		if err != nil {
			log.Fatal(err)
		}
		defer input.Close()
	}

	bytes, err := ioutil.ReadAll(input)
	if err != nil {
		log.Fatal(err)
	}

	cfg, err = cfg.Import(bytes)
	if err != nil {
		log.Fatal(err)
	}

	if err := cfg.Save(); err != nil {
		log.Fatal(err)
	}
}
Пример #7
0
func SysInit() error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	_, err = config.ChainCfgFuncs(cfg,
		loadImages,
		func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
			p, err := compose.GetProject(cfg, false)
			if err != nil {
				return cfg, err
			}
			return cfg, p.Up()
		},
		func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
			syscall.Sync()
			return cfg, nil
		},
		func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
			log.Infof("RancherOS %s started", config.VERSION)
			return cfg, nil
		})
	return err
}
Пример #8
0
func executeCloudConfig() error {
	cc, err := rancherConfig.LoadConfig()
	if err != nil {
		return err
	}

	if _, err := SetHostname(cc); err != nil {
		return err
	}

	if len(cc.SSHAuthorizedKeys) > 0 {
		authorizeSSHKeys("rancher", cc.SSHAuthorizedKeys, sshKeyName)
		authorizeSSHKeys("docker", cc.SSHAuthorizedKeys, sshKeyName)
	}

	for _, file := range cc.WriteFiles {
		f := system.File{File: file}
		fullPath, err := system.WriteFile(&f, "/")
		if err != nil {
			log.WithFields(log.Fields{"err": err, "path": fullPath}).Error("Error writing file")
			continue
		}
		log.Printf("Wrote file %s to filesystem", fullPath)
	}

	return nil
}
Пример #9
0
Файл: config.go Проект: Jdesk/os
func configGet(c *cli.Context) {
	arg := c.Args().Get(0)
	if arg == "" {
		return
	}

	cfg, err := config.LoadConfig()
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("config get: failed to load config")
	}

	val, err := cfg.Get(arg)
	if err != nil {
		log.WithFields(log.Fields{"cfg": cfg, "key": arg, "val": val, "err": err}).Fatal("config get: failed to retrieve value")
	}

	printYaml := false
	switch val.(type) {
	case []interface{}:
		printYaml = true
	case map[interface{}]interface{}:
		printYaml = true
	}

	if printYaml {
		bytes, err := yaml.Marshal(val)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(string(bytes))
	} else {
		fmt.Println(val)
	}
}
Пример #10
0
Файл: env.go Проект: pirater/os
func envAction(c *cli.Context) {
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	args := c.Args()
	if len(args) == 0 {
		return
	}
	osEnv := os.Environ()

	envMap := make(map[string]string, len(cfg.Rancher.Environment)+len(osEnv))
	for k, v := range cfg.Rancher.Environment {
		envMap[k] = v
	}
	for k, v := range util.KVPairs2Map(osEnv) {
		envMap[k] = v
	}

	if cmd, err := exec.LookPath(args[0]); err != nil {
		log.Fatal(err)
	} else {
		args[0] = cmd
	}
	if err := syscall.Exec(args[0], args, util.Map2KVPairs(envMap)); err != nil {
		log.Fatal(err)
	}
}
Пример #11
0
func loadFromNetwork(location string) ([]byte, error) {
	bytes := cacheLookup(location)
	if bytes != nil {
		return bytes, nil
	}

	cfg := config.LoadConfig()
	SetProxyEnvironmentVariables(cfg)

	var err error
	for i := 0; i < 300; i++ {
		updateDNSCache()

		var resp *http.Response
		resp, err = http.Get(location)
		if err == nil {
			defer resp.Body.Close()
			if resp.StatusCode != http.StatusOK {
				return nil, fmt.Errorf("non-200 http response: %d", resp.StatusCode)
			}

			bytes, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return nil, err
			}

			cacheAdd(location, bytes)
			return bytes, nil
		}

		time.Sleep(100 * time.Millisecond)
	}

	return nil, err
}
Пример #12
0
func Main() {
	if len(os.Args) != 2 {
		log.Fatal("Must specify exactly one existing container")
	}
	newConsole := os.Args[1]

	cfg := config.LoadConfig()

	project, err := compose.GetProject(cfg, true, false)
	if err != nil {
		log.Fatal(err)
	}

	if newConsole != "default" {
		if err = compose.LoadService(project, cfg, true, newConsole); err != nil {
			log.Fatal(err)
		}
	}

	if err = config.Set("rancher.console", newConsole); err != nil {
		log.Errorf("Failed to update 'rancher.console': %v", err)
	}

	if err = project.Up(context.Background(), options.Up{
		Log: true,
	}, "console"); err != nil {
		log.Fatal(err)
	}

	if err = project.Restart(context.Background(), 10, "docker"); err != nil {
		log.Errorf("Failed to restart Docker: %v", err)
	}
}
Пример #13
0
Файл: os.go Проект: datawolf/os
func getUpgradeUrl() (string, error) {
	cfg, err := config.LoadConfig()
	if err != nil {
		return "", err
	}

	return cfg.Rancher.Upgrade.Url, nil
}
Пример #14
0
func (p *projectFactory) Create(c *cli.Context) (*project.Project, error) {
	cfg, err := config.LoadConfig()
	if err != nil {
		return nil, err
	}

	return compose.GetProject(cfg, true)
}
Пример #15
0
func mountOem(cfg *config.CloudConfig) (*config.CloudConfig, error) {
	if cfg == nil {
		cfg = config.LoadConfig()
	}
	if err := mountConfigured("oem", cfg.Rancher.State.OemDev, cfg.Rancher.State.OemFsType, config.OEM); err != nil {
		log.Debugf("Not mounting OEM: %v", err)
	} else {
		log.Infof("Mounted OEM: %s", cfg.Rancher.State.OemDev)
	}
	return cfg, nil
}
Пример #16
0
func Main() {
	args := os.Args
	if len(args) > 1 {
		fmt.Println("call " + args[0] + " to load network config from cloud-config.yml")
		return
	}
	os.Remove(NETWORK_DONE) // ignore error
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}
	hostname, _ := cloudinit.SetHostname(cfg) // ignore error
	log.Infof("Network: hostname: '%s'", hostname)
	if err := netconf.ApplyNetworkConfigs(&cfg.Rancher.Network); err != nil {
		log.Error(err)
	}
	hostname, _ = cloudinit.SetHostname(cfg) // ignore error
	log.Infof("Network: hostname: '%s' (from DHCP, if not set by cloud-config)", hostname)
	if hostname != "" {
		hosts, err := os.Open("/etc/hosts")
		defer hosts.Close()
		if err != nil {
			log.Fatal(err)
		}
		lines := bufio.NewScanner(hosts)
		hostsContent := ""
		for lines.Scan() {
			line := strings.TrimSpace(lines.Text())
			fields := strings.Fields(line)
			if len(fields) > 0 && fields[0] == "127.0.1.1" {
				hostsContent += "127.0.1.1 " + hostname + "\n"
				continue
			}
			hostsContent += line + "\n"
		}
		if err := ioutil.WriteFile("/etc/hosts", []byte(hostsContent), 0600); err != nil {
			log.Error(err)
		}
	}
	if cfg.Rancher.Network.Dns.Override {
		log.WithFields(log.Fields{"nameservers": cfg.Rancher.Network.Dns.Nameservers}).Info("Override nameservers")
		if _, err := resolvconf.Build("/etc/resolv.conf", cfg.Rancher.Network.Dns.Nameservers, cfg.Rancher.Network.Dns.Search, nil); err != nil {
			log.Error(err)
		}
	}
	if f, err := os.Create(NETWORK_DONE); err != nil {
		log.Error(err)
	} else {
		f.Close()
	}
	sendTerm(WAIT_FOR_NETWORK)
	select {}
}
Пример #17
0
Файл: init.go Проект: pirater/os
func mountOem(cfg *config.CloudConfig) (*config.CloudConfig, error) {
	if cfg == nil {
		var err error
		if cfg, err = config.LoadConfig(); err != nil {
			return cfg, err
		}
	}
	if err := mountConfigured("oem", cfg.Rancher.State.OemDev, cfg.Rancher.State.OemFsType, config.OEM); err != nil {
		log.Infof("Not mounting OEM: %v", err)
	}
	return cfg, nil
}
Пример #18
0
func (s *Service) shouldRebuild(ctx context.Context) (bool, error) {
	containers, err := s.Containers(ctx)
	if err != nil {
		return false, err
	}
	cfg := config.LoadConfig()
	for _, c := range containers {
		outOfSync, err := c.(*docker.Container).OutOfSync(ctx, s.Service.Config().Image)
		if err != nil {
			return false, err
		}

		_, containerInfo, err := s.getContainer(ctx)
		if err != nil {
			return false, err
		}
		name := containerInfo.Name[1:]

		origRebuildLabel := containerInfo.Config.Labels[config.REBUILD]
		newRebuildLabel := s.Config().Labels[config.REBUILD]
		rebuildLabelChanged := newRebuildLabel != origRebuildLabel
		logrus.WithFields(logrus.Fields{
			"origRebuildLabel":    origRebuildLabel,
			"newRebuildLabel":     newRebuildLabel,
			"rebuildLabelChanged": rebuildLabelChanged,
			"outOfSync":           outOfSync}).Debug("Rebuild values")

		if newRebuildLabel == "always" {
			return true, nil
		}
		if outOfSync {
			if s.Name() == "console" {
				if cfg.Rancher.ForceConsoleRebuild {
					if err := config.Set("rancher.force_console_rebuild", false); err != nil {
						return false, err
					}
					return true, nil
				}
				origConsoleLabel := containerInfo.Config.Labels[config.CONSOLE]
				newConsoleLabel := s.Config().Labels[config.CONSOLE]
				if newConsoleLabel != origConsoleLabel {
					return true, nil
				}
			} else if rebuildLabelChanged || origRebuildLabel != "false" {
				return true, nil
			} else {
				logrus.Warnf("%s needs rebuilding", name)
			}
		}
	}
	return false, nil
}
Пример #19
0
func Main() {
	flags.Parse(os.Args[1:])

	log.Infof("Running network: daemon=%v", daemon)

	os.Remove(NETWORK_DONE) // ignore error
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	nameservers := cfg.Rancher.Network.Dns.Nameservers
	search := cfg.Rancher.Network.Dns.Search
	userSetDns := len(nameservers) > 0 || len(search) > 0
	if !userSetDns {
		nameservers = cfg.Rancher.DefaultNetwork.Dns.Nameservers
		search = cfg.Rancher.DefaultNetwork.Dns.Search
	}

	if _, err := resolvconf.Build("/etc/resolv.conf", nameservers, search, nil); err != nil {
		log.Error(err)
	}

	if err := hostname.SetHostnameFromCloudConfig(cfg); err != nil {
		log.Error(err)
	}

	if err := netconf.ApplyNetworkConfigs(&cfg.Rancher.Network); err != nil {
		log.Error(err)
	}

	userSetHostname := cfg.Hostname != ""
	if err := netconf.RunDhcp(&cfg.Rancher.Network, !userSetHostname, !userSetDns); err != nil {
		log.Error(err)
	}

	if err := hostname.SyncHostname(); err != nil {
		log.Error(err)
	}

	if f, err := os.Create(NETWORK_DONE); err != nil {
		log.Error(err)
	} else {
		f.Close()
	}
	sendTerm(WAIT_FOR_NETWORK)

	if daemon {
		select {}
	}
}
Пример #20
0
func Main() {
	args := os.Args
	if len(args) > 1 {
		fmt.Println("call " + args[0] + " to load network config from cloud-config.yml")
		return
	}
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}
	if err := netconf.ApplyNetworkConfigs(&cfg.Rancher.Network); err != nil {
		log.Fatal(err)
	}
}
Пример #21
0
func CreateService(cfg *config.CloudConfig, name string, serviceConfig *composeConfig.ServiceConfigV1) (project.Service, error) {
	if cfg == nil {
		cfg = config.LoadConfig()
	}

	p, err := CreateServiceSet("once", cfg, map[string]*composeConfig.ServiceConfigV1{
		name: serviceConfig,
	})
	if err != nil {
		return nil, err
	}

	return p.CreateService(name)
}
Пример #22
0
func consoleSwitch(c *cli.Context) error {
	if len(c.Args()) != 1 {
		log.Fatal("Must specify exactly one console to switch to")
	}
	newConsole := c.Args()[0]

	cfg := config.LoadConfig()
	if newConsole == cfg.Rancher.Console {
		log.Warnf("Console is already set to %s", newConsole)
	}

	if !c.Bool("force") {
		in := bufio.NewReader(os.Stdin)
		fmt.Println("Switching consoles will destroy the current console container and restart Docker.")
		fmt.Println("Note: You will also be logged out.")
		if !yes(in, "Continue") {
			return nil
		}
	}

	if newConsole != "default" {
		if err := compose.StageServices(cfg, newConsole); err != nil {
			return err
		}
	}

	service, err := compose.CreateService(nil, "switch-console", &composeConfig.ServiceConfigV1{
		LogDriver:  "json-file",
		Privileged: true,
		Net:        "host",
		Pid:        "host",
		Image:      fmt.Sprintf("rancher/os-base:%s", config.VERSION),
		Labels: map[string]string{
			config.SCOPE: config.SYSTEM,
		},
		Command:     []string{"/usr/bin/switch-console", newConsole},
		VolumesFrom: []string{"all-volumes"},
	})
	if err != nil {
		return err
	}

	if err = service.Delete(context.Background(), options.Delete{}); err != nil {
		return err
	}
	if err = service.Up(context.Background(), options.Up{}); err != nil {
		return err
	}
	return service.Log(context.Background(), true)
}
Пример #23
0
func consoleList(c *cli.Context) error {
	cfg := config.LoadConfig()

	consoles, err := network.GetConsoles(cfg.Rancher.Repositories.ToArray())
	if err != nil {
		return err
	}

	fmt.Println("default")
	for _, console := range consoles {
		fmt.Println(console)
	}

	return nil
}
Пример #24
0
func currentDatasource() (datasource.Datasource, error) {
	cfg, err := rancherConfig.LoadConfig()
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Error("Failed to read rancher config")
		return nil, err
	}

	dss := getDatasources(cfg)
	if len(dss) == 0 {
		return nil, nil
	}

	ds := selectDatasource(dss)
	return ds, nil
}
Пример #25
0
func Generate(generateServer bool, outDir string, hostnames []string) error {
	cfg, err := config.LoadConfig()
	if err != nil {
		return err
	}

	if outDir == "" {
		if generateServer {
			outDir = "/etc/docker/tls"
		} else {
			outDir = "/home/rancher/.docker"
		}
		log.Infof("Out directory (-d, --dir) not specified, using default: %s", outDir)
	}
	caCertPath := filepath.Join(outDir, "ca.pem")
	caKeyPath := filepath.Join(outDir, "ca-key.pem")
	certPath := filepath.Join(outDir, "cert.pem")
	keyPath := filepath.Join(outDir, "key.pem")

	if generateServer {
		certPath = filepath.Join(outDir, "server-cert.pem")
		keyPath = filepath.Join(outDir, "server-key.pem")
	}

	if _, err := os.Stat(outDir); os.IsNotExist(err) {
		if err := os.MkdirAll(outDir, 0700); err != nil {
			return err
		}
	}

	cfg, err = writeCaCerts(cfg, caCertPath, caKeyPath)
	if err != nil {
		return err
	}
	if err := writeCerts(generateServer, hostnames, cfg, certPath, keyPath, caCertPath, caKeyPath); err != nil {
		return err
	}

	if !generateServer {
		if err := filepath.Walk(outDir, func(path string, info os.FileInfo, err error) error {
			return os.Chown(path, 1100, 1100) // rancher:rancher
		}); err != nil {
			return err
		}
	}

	return nil
}
Пример #26
0
func Main() {
	args := os.Args
	if len(args) > 1 {
		fmt.Println("call " + args[0] + " to load network config from cloud-config.yml")
		return
	}
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}
	// Purposely ignore error
	cloudinit.SetHostname(cfg)
	if err := netconf.ApplyNetworkConfigs(&cfg.Rancher.Network); err != nil {
		log.Fatal(err)
	}
}
Пример #27
0
Файл: main.go Проект: Jdesk/os
func Main() {
	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	if len(os.Args) == 1 {
		if err := enter(cfg); err != nil {
			log.Fatal(err)
		}
	} else {
		if err := main(cfg); err != nil {
			log.Fatal(err)
		}
	}
}
Пример #28
0
func CreateService(cfg *config.CloudConfig, name string, serviceConfig *project.ServiceConfig) (project.Service, error) {
	if cfg == nil {
		var err error
		cfg, err = config.LoadConfig()
		if err != nil {
			return nil, err
		}
	}

	p, err := CreateServiceSet("once", cfg, map[string]*project.ServiceConfig{
		name: serviceConfig,
	})
	if err != nil {
		return nil, err
	}

	return p.CreateService(name)
}
Пример #29
0
func installAction(c *cli.Context) {
	if c.Args().Present() {
		log.Fatalf("invalid arguments %v", c.Args())
	}
	device := c.String("device")
	if device == "" {
		log.Fatal("Can not proceed without -d <dev> specified")
	}

	image := c.String("image")
	cfg, err := config.LoadConfig()
	if err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("ros install: failed to load config")
	}
	if image == "" {
		image = cfg.Rancher.Upgrade.Image + ":" + config.VERSION
	}

	installType := c.String("install-type")
	if installType == "" {
		log.Info("No install type specified...defaulting to generic")
		installType = "generic"
	}

	cloudConfig := c.String("cloud-config")
	if cloudConfig == "" {
		log.Warn("Cloud-config not provided: you might need to provide cloud-config on boot with ssh_authorized_keys")
	} else {
		uc := "/opt/user_config.yml"
		if err := util.FileCopy(cloudConfig, uc); err != nil {
			log.WithFields(log.Fields{"cloudConfig": cloudConfig}).Fatal("Failed to copy cloud-config")
		}
		cloudConfig = uc
	}

	force := c.Bool("force")
	reboot := !c.Bool("no-reboot")

	if err := runInstall(image, installType, cloudConfig, device, force, reboot); err != nil {
		log.WithFields(log.Fields{"err": err}).Fatal("Failed to run install")
	}
}
Пример #30
0
Файл: config.go Проект: Jdesk/os
func merge(c *cli.Context) {
	bytes, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		log.Fatal(err)
	}

	cfg, err := config.LoadConfig()
	if err != nil {
		log.Fatal(err)
	}

	cfg, err = cfg.MergeBytes(bytes)
	if err != nil {
		log.Fatal(err)
	}

	if err := cfg.Save(); err != nil {
		log.Fatal(err)
	}
}