func RmChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { if err = perform.DockerRemove(chain.Service, chain.Operations, do.RmD); err != nil { return err } } else { logger.Infoln("That chain's container does not exist.") } if do.File { oldFile := util.GetFileByNameAndType("chains", do.Name) if err != nil { return err } oldFile = path.Join(BlockchainsPath, oldFile) + ".toml" logger.Printf("Removing file =>\t\t%s\n", oldFile) if err := os.Remove(oldFile); err != nil { return err } } return nil }
func KillChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if do.Force { do.Timeout = 0 //overrides 10 sec default } if IsChainRunning(chain) { if err := perform.DockerStop(chain.Service, chain.Operations, do.Timeout); err != nil { return err } } else { logger.Infoln("Chain not currently running. Skipping.") } if do.Rm { if err := perform.DockerRemove(chain.Service, chain.Operations, do.RmD, do.Volumes); err != nil { return err } } return nil }
// boot chain dependencies // TODO: this currently only supports simple services (with no further dependencies) func bootDependencies(chain *definitions.Chain, do *definitions.Do) error { if chain.Dependencies != nil { name := do.Name logger.Infoln("Booting chain dependencies", chain.Dependencies.Services, chain.Dependencies.Chains) for _, srvName := range chain.Dependencies.Services { do.Name = srvName srv, err := loaders.LoadServiceDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } // Start corresponding service. if !services.IsServiceRunning(srv.Service, srv.Operations) { name := strings.ToUpper(do.Name) logger.Infof("%s is not running. Starting now. Waiting for %s to become available \n", name, name) if err = perform.DockerRunService(srv.Service, srv.Operations); err != nil { return err } } } do.Name = name // undo side effects for _, chainName := range chain.Dependencies.Chains { chn, err := loaders.LoadChainDefinition(chainName, false, do.Operations.ContainerNumber) if err != nil { return err } if !IsChainRunning(chn) { return fmt.Errorf("chain %s depends on chain %s but %s is not running", chain.Name, chainName, chainName) } } } return nil }
func RmChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { if err = perform.DockerRemove(chain.Service, chain.Operations, do.RmD, do.Volumes); err != nil { return err } } else { log.Info("Chain container does not exist") } if do.File { oldFile := util.GetFileByNameAndType("chains", do.Name) if err != nil { return err } log.WithField("file", oldFile).Warn("Removing file") if err := os.Remove(oldFile); err != nil { return err } } return nil }
func GraduateChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, 1) if err != nil { return err } serv := loaders.ServiceDefFromChain(chain, loaders.ErisChainStart) if err := services.WriteServiceDefinitionFile(serv, filepath.Join(ServicesPath, chain.ChainID+".toml")); err != nil { return err } return nil }
func LogsChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } err = perform.DockerLogs(chain.Service, chain.Operations, do.Follow, do.Tail) if err != nil { return err } return nil }
func PortsChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { logger.Debugf("Chain exists, getting port mapping.\n") return perform.PrintPortMappings(chain.Operations.SrvContainerID, do.Args) } return nil }
func PortsChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { log.WithField("=>", chain.Name).Debug("Getting chain port mapping") return util.PrintPortMappings(chain.Operations.SrvContainerID, do.Operations.Args) } return nil }
func RegisterChain(do *definitions.Do) error { // do.Name is mandatory if do.Name == "" { return fmt.Errorf("RegisterChain requires a chainame") } etcbChain := do.ChainID do.ChainID = do.Name // NOTE: registration expects you to have the data container if !util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { return fmt.Errorf("Registration requires you to have a data container for the chain. Could not find data for %s", do.Name) } chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } log.WithField("image", chain.Service.Image).Debug("Chain loaded") // set chainid and other vars envVars := []string{ fmt.Sprintf("CHAIN_ID=%s", do.ChainID), // of the etcb chain fmt.Sprintf("PUBKEY=%s", do.Pubkey), // pubkey to register chain with fmt.Sprintf("ETCB_CHAIN_ID=%s", etcbChain), // chain id of the etcb chain fmt.Sprintf("NODE_ADDR=%s", do.Gateway), // etcb node to send the register tx to fmt.Sprintf("NEW_P2P_SEEDS=%s", do.Operations.Args[0]), // seeds to register for the chain // TODO: deal with multi seed (needs support in tendermint) } envVars = append(envVars, do.Env...) log.WithFields(log.Fields{ "environment": envVars, "links": do.Links, }).Debug("Registering chain with") chain.Service.Environment = append(chain.Service.Environment, envVars...) chain.Service.Links = append(chain.Service.Links, do.Links...) if err := bootDependencies(chain, do); err != nil { return err } log.WithFields(log.Fields{ "=>": chain.Service.Name, "image": chain.Service.Image, }).Debug("Performing chain container start") chain.Operations = loaders.LoadDataDefinition(chain.Service.Name, do.Operations.ContainerNumber) chain.Operations.Args = []string{loaders.ErisChainRegister} _, err = perform.DockerRunData(chain.Operations, chain.Service) return err }
func ExecChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { logger.Infoln("Chain exists.") return perform.DockerExec(chain.Service, chain.Operations, do.Args, do.Interactive) } else { return fmt.Errorf("Chain does not exist. Please start the chain container with eris chains start %s.\n", do.Name) } return nil }
func InspectChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { log.WithField("=>", chain.Service.Name).Debug("Inspecting chain") err := services.InspectServiceByService(chain.Service, chain.Operations, do.Operations.Args[0]) if err != nil { return err } } return nil }
func InspectChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { logger.Debugf("Chain exists, calling services.InspectServiceByService.\n") err := services.InspectServiceByService(chain.Service, chain.Operations, do.Args[0]) if err != nil { return err } } return nil }
func UpdateChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } // DockerRebuild is built for services, adding false to the final // variable will mean it pulls. But we want the opposite default // behaviour for chains as we do for services in this regard // so we flip the variable. err = perform.DockerRebuild(chain.Service, chain.Operations, do.SkipPull, do.Timeout) if err != nil { return err } return nil }
func RegisterChain(do *definitions.Do) error { // do.Name is mandatory if do.Name == "" { return fmt.Errorf("RegisterChain requires a chainame") } etcbChain := do.ChainID do.ChainID = do.Name // NOTE: registration expects you to have the data container if !data.IsKnown(do.Name) { return fmt.Errorf("Registration requires you to have a data container for the chain. Could not find data for %s", do.Name) } chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } logger.Debugf("Chain Loaded. Image =>\t\t%v\n", chain.Service.Image) // set chainid and other vars envVars := []string{ fmt.Sprintf("CHAIN_ID=%s", do.ChainID), // of the etcb chain fmt.Sprintf("PUBKEY=%s", do.Pubkey), // pubkey to register chain with fmt.Sprintf("ETCB_CHAIN_ID=%s", etcbChain), // chain id of the etcb chain fmt.Sprintf("NODE_ADDR=%s", do.Gateway), // etcb node to send the register tx to fmt.Sprintf("NEW_P2P_SEEDS=%s", do.Args[0]), // seeds to register for the chain // TODO: deal with multi seed (needs support in tendermint) } envVars = append(envVars, do.Env...) logger.Debugf("Set env vars from RegisterChain =>\t%v\n", envVars) chain.Service.Environment = append(chain.Service.Environment, envVars...) logger.Debugf("Set links from RegisterChain =>\t%v\n", do.Links) chain.Service.Links = append(chain.Service.Links, do.Links...) if err := bootDependencies(chain, do); err != nil { return err } logger.Debugf("Starting chain container via Docker =>\t%s\n", chain.Service.Name) logger.Debugf("\twith Image =>\t\t%s\n", chain.Service.Image) cmd := []string{loaders.ErisChainRegister} dataContainerName := util.DataContainersName(chain.Name, chain.Operations.ContainerNumber) _, err = perform.DockerRunVolumesFromContainer(dataContainerName, false, cmd, chain.Service) return err }
func StartChain(do *definitions.Do) error { logger.Infoln("Ensuring Key Server is Started.") //should it take a flag? keys server may be running another cNum // XXX: currently we don't use or need a key server. // plus this should be specified in a service def anyways keysService, err := loaders.LoadServiceDefinition("keys", false, 1) if err != nil { return err } err = perform.DockerRun(keysService.Service, keysService.Operations) if err != nil { return err } chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { logger.Infoln("Cannot start a chain I cannot find.") do.Result = "no file" return nil } if chain.Name == "" { logger.Infoln("Cannot start a chain without a name.") do.Result = "no name" return nil } chain.Service.Command = loaders.ErisChainStart if do.Run { chain.Service.Command = loaders.ErisChainStartApi } util.OverWriteOperations(chain.Operations, do.Operations) chain.Service.Environment = append(chain.Service.Environment, "CHAIN_ID="+chain.ChainID) logger.Infof("StartChainRaw to DockerRun =>\t%s\n", chain.Service.Name) logger.Debugf("\twith ChainID =>\t\t%v\n", chain.ChainID) logger.Debugf("\twith Environment =>\t%v\n", chain.Service.Environment) logger.Debugf("\twith AllPortsPublshd =>\t%v\n", chain.Operations.PublishAllPorts) if err := perform.DockerRun(chain.Service, chain.Operations); err != nil { do.Result = "error" return err } return nil }
func UpdateChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } // if the chain is already running we need to set the right env vars and command if IsChainRunning(chain) { chain.Service.Environment = []string{fmt.Sprintf("CHAIN_ID=%s", do.Name)} chain.Service.Command = loaders.ErisChainStart } err = perform.DockerRebuild(chain.Service, chain.Operations, do.SkipPull, do.Timeout) if err != nil { return err } return nil }
func TestLoadChainDefinition(t *testing.T) { var e error logger.Infof("Load chain def (from tests) =>\t%s\n", chainName) chn, e := loaders.LoadChainDefinition(chainName, false, 1) if e != nil { fatal(t, e) } if chn.Service.Name != chainName { fatal(t, fmt.Errorf("FAILURE: improper service name on LOAD. expected: %s\tgot: %s", chainName, chn.Service.Name)) } if !chn.Service.AutoData { fatal(t, fmt.Errorf("FAILURE: data_container not properly read on LOAD.")) } if chn.Operations.DataContainerName == "" { fatal(t, fmt.Errorf("FAILURE: data_container_name not set.")) } }
func TestLoadChainDefinition(t *testing.T) { var e error log.WithField("=>", chainName).Info("Load chain definition (from tests)") chn, e := loaders.LoadChainDefinition(chainName, false, 1) if e != nil { tests.IfExit(e) } if chn.Service.Name != chainName { tests.IfExit(fmt.Errorf("FAILURE: improper service name on LOAD. expected: %s\tgot: %s", chainName, chn.Service.Name)) } if !chn.Service.AutoData { tests.IfExit(fmt.Errorf("FAILURE: data_container not properly read on LOAD.")) } if chn.Operations.DataContainerName == "" { tests.IfExit(fmt.Errorf("FAILURE: data_container_name not set.")) } }
func TestLoadChainDefinition(t *testing.T) { // [pv]: this test belongs to the loaders package. var err error chain, err := loaders.LoadChainDefinition(chainName, false, 1) if err != nil { t.Fatalf("expected chain definition to be loaded, got %v", err) } if chain.Service.Name != chainName { t.Fatalf("improper service name on load, expected %s, got %s", chainName, chain.Service.Name) } if !chain.Service.AutoData { t.Fatalf("data_container not properly read on load, expected false") } if chain.Operations.DataContainerName == "" { t.Fatalf("data_container_name not set") } }
// boot chain dependencies // TODO: this currently only supports simple services (with no further dependencies) func bootDependencies(chain *definitions.Chain, do *definitions.Do) error { if do.Logsrotate { chain.Dependencies.Services = append(chain.Dependencies.Services, "logsrotate") } if chain.Dependencies != nil { name := do.Name log.WithFields(log.Fields{ "services": chain.Dependencies.Services, "chains": chain.Dependencies.Chains, }).Info("Booting chain dependencies") for _, srvName := range chain.Dependencies.Services { do.Name = srvName srv, err := loaders.LoadServiceDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } // Start corresponding service. if !services.IsServiceRunning(srv.Service, srv.Operations) { name := strings.ToUpper(do.Name) log.WithField("=>", name).Info("Dependency not running. Starting now") if err = perform.DockerRunService(srv.Service, srv.Operations); err != nil { return err } } } do.Name = name // undo side effects for _, chainName := range chain.Dependencies.Chains { chn, err := loaders.LoadChainDefinition(chainName, false, do.Operations.ContainerNumber) if err != nil { return err } if !IsChainRunning(chn) { return fmt.Errorf("chain %s depends on chain %s but %s is not running", chain.Name, chainName, chainName) } } } return nil }
func StartChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { logger.Infoln("Cannot start a chain I cannot find.") do.Result = "no file" return nil } if chain.Name == "" { logger.Infoln("Cannot start a chain without a name.") do.Result = "no name" return nil } // boot the dependencies (eg. keys) if err := bootDependencies(chain, do); err != nil { return err } chain.Service.Command = loaders.ErisChainStart util.OverWriteOperations(chain.Operations, do.Operations) chain.Service.Environment = append(chain.Service.Environment, "CHAIN_ID="+chain.ChainID) chain.Service.Environment = append(chain.Service.Environment, do.Env...) if do.Run { chain.Service.Environment = append(chain.Service.Environment, "ERISDB_API=true") } chain.Service.Links = append(chain.Service.Links, do.Links...) logger.Infof("StartChainRaw to DockerRun =>\t%s\n", chain.Service.Name) logger.Debugf("\twith ChainID =>\t\t%v\n", chain.ChainID) logger.Debugf("\twith Environment =>\t%v\n", chain.Service.Environment) logger.Debugf("\twith AllPortsPublshd =>\t%v\n", chain.Operations.PublishAllPorts) if _, err := perform.DockerRun(chain.Service, chain.Operations); err != nil { do.Result = "error" return err } return nil }
// export a chain definition file func ExportChain(do *definitions.Do) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } if IsChainExisting(chain) { doNow := definitions.NowDo() doNow.Name = "ipfs" services.EnsureRunning(doNow) hash, err := exportFile(do.Name) if err != nil { return err } log.Warn(hash) } else { return fmt.Errorf(`I don't known of that chain. Please retry with a known chain. To find known chains use: eris chains ls --known`) } return nil }
// the main function for setting up a chain container // handles both "new" and "fetch" - most of the differentiating logic is in the container func setupChain(do *definitions.Do, cmd string) (err error) { // XXX: if do.Name is unique, we can safely assume (and we probably should) that do.Operations.ContainerNumber = 1 // do.Name is mandatory if do.Name == "" { return fmt.Errorf("setupChain requires a chainame") } containerName := util.ChainContainersName(do.Name, do.Operations.ContainerNumber) if do.ChainID == "" { do.ChainID = do.Name } // ensure/create data container if !data.IsKnown(containerName) { if err := perform.DockerCreateDataContainer(do.Name, do.Operations.ContainerNumber); err != nil { return fmt.Errorf("Error creating data containr =>\t%v", err) } } else { logger.Debugln("Data container already exists for", do.Name) } logger.Debugf("Chain's Data Contain Built =>\t%s\n", do.Name) // if something goes wrong, cleanup defer func() { if err != nil { logger.Infof("Error on setupChain =>\t\t%v\n", err) logger.Infoln("Cleaning up...") if err2 := RmChain(do); err2 != nil { // maybe be less dramatic err = fmt.Errorf("Tragic! Our marmots encountered an error during setupChain for %s.\nThey also failed to cleanup after themselves (remove containers) due to another error.\nFirst error =>\t\t\t%v\nCleanup error =>\t\t%v\n", containerName, err, err2) } } }() // copy do.Path, do.GenesisFile, do.ConfigFile, do.Priv, do.CSV into container containerDst := path.Join("blockchains", do.Name) // path in container dst := path.Join(DataContainersPath, do.Name, containerDst) // path on host // TODO: deal with do.Operations.ContainerNumbers ....! // we probably need to update Import logger.Debugln("container destination:", containerDst) logger.Debugln("local destination:", dst) if err = os.MkdirAll(dst, 0700); err != nil { return fmt.Errorf("Error making data directory: %v", err) } var csvFile, csvPath string if do.CSV != "" { csvFile = "genesis.csv" csvPath = fmt.Sprintf("/home/eris/.eris/blockchains/%s/%s", do.ChainID, csvFile) } if err := copyFiles(dst, []stringPair{ {do.Path, ""}, {do.GenesisFile, "genesis.json"}, {do.ConfigFile, "config.toml"}, {do.Priv, "priv_validator.json"}, {do.CSV, csvFile}, }); err != nil { return err } // copy from host to container logger.Debugf("Copying Files into DataCont =>\t%s:%s\n", dst, containerDst) importDo := definitions.NowDo() importDo.Name = do.Name importDo.Operations = do.Operations if err = data.ImportData(importDo); err != nil { return err } chain := loaders.MockChainDefinition(do.Name, do.ChainID, false, do.Operations.ContainerNumber) //set maintainer info chain.Maintainer.Name, chain.Maintainer.Email, err = config.GitConfigUser() if err != nil { logger.Debugf(err.Error()) } // write the chain definition file ... fileName := filepath.Join(BlockchainsPath, do.Name) + ".toml" if _, err = os.Stat(fileName); err != nil { if err = WriteChainDefinitionFile(chain, fileName); err != nil { return fmt.Errorf("error writing chain definition to file: %v", err) } } chain, err = loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } logger.Debugf("Chain Loaded. Image =>\t\t%v\n", chain.Service.Image) chain.Operations.PublishAllPorts = do.Operations.PublishAllPorts // TODO: remove this and marshall into struct from cli directly // cmd should be "new" or "install" chain.Service.Command = cmd // do we need to create our own do.GenesisFile? var genGen bool if do.GenesisFile == "" { genGen = true } // write the list of <key>:<value> config options as flags buf := new(bytes.Buffer) for _, cv := range do.ConfigOpts { spl := strings.Split(cv, "=") if len(spl) != 2 { return fmt.Errorf("Config options should be <key>=<value> pairs. Got %s", cv) } buf.WriteString(fmt.Sprintf(" --%s=%s", spl[0], spl[1])) } configOpts := buf.String() // set chainid and other vars envVars := []string{ fmt.Sprintf("CHAIN_ID=%s", do.ChainID), fmt.Sprintf("CONTAINER_NAME=%s", containerName), fmt.Sprintf("RUN=%v", do.Run), fmt.Sprintf("GENERATE_GENESIS=%v", genGen), fmt.Sprintf("CSV=%v", csvPath), fmt.Sprintf("CONFIG_OPTS=%s", configOpts), } logger.Debugf("Set env vars from setupChain =>\t%v\n", envVars) for _, eV := range envVars { chain.Service.Environment = append(chain.Service.Environment, eV) } // TODO: if do.N > 1 ... chain.Operations.DataContainerName = util.DataContainersName(do.Name, do.Operations.ContainerNumber) if os.Getenv("TEST_IN_CIRCLE") != "true" { chain.Operations.Remove = true } logger.Debugf("Starting chain via Docker =>\t%s\n", chain.Service.Name) logger.Debugf("\twith Image =>\t\t%s\n", chain.Service.Image) err = perform.DockerRun(chain.Service, chain.Operations) // this err is caught in the defer above return }
// the main function for setting up a chain container // handles both "new" and "fetch" - most of the differentiating logic is in the container func setupChain(do *definitions.Do, cmd string) (err error) { // XXX: if do.Name is unique, we can safely assume (and we probably should) that do.Operations.ContainerNumber = 1 // do.Name is mandatory if do.Name == "" { return fmt.Errorf("setupChain requires a chainame") } containerName := util.ChainContainersName(do.Name, do.Operations.ContainerNumber) if do.ChainID == "" { do.ChainID = do.Name } //if given path does not exist, see if its a reference to something in ~/.eris/chains/chainName if do.Path != "" { src, err := os.Stat(do.Path) if err != nil || !src.IsDir() { logger.Infof("Path (%s) does not exist or is not a directory, trying $HOME/.eris/chains/%s\n", do.Path, do.Path) do.Path, err = util.ChainsPathChecker(do.Path) if err != nil { return err } } } else if do.GenesisFile == "" && do.CSV == "" && len(do.ConfigOpts) == 0 { // NOTE: this expects you to have ~/.eris/chains/default/ (ie. to have run `eris init`) do.Path, err = util.ChainsPathChecker("default") if err != nil { return err } } // ensure/create data container if !data.IsKnown(do.Name) { ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) if err := perform.DockerCreateData(ops); err != nil { return fmt.Errorf("Error creating data container =>\t%v", err) } } else { logger.Debugln("Data container already exists for", do.Name) } logger.Debugf("Chain's Data Contain Built =>\t%s\n", do.Name) // if something goes wrong, cleanup defer func() { if err != nil { logger.Infof("Error on setupChain =>\t\t%v\n", err) logger.Infoln("Cleaning up...") if err2 := RmChain(do); err2 != nil { // maybe be less dramatic err = fmt.Errorf("Tragic! Our marmots encountered an error during setupChain for %s.\nThey also failed to cleanup after themselves (remove containers) due to another error.\nFirst error =>\t\t\t%v\nCleanup error =>\t\t%v\n", containerName, err, err2) } } }() // copy do.Path, do.GenesisFile, do.ConfigFile, do.Priv, do.CSV into container containerDst := path.Join("chains", do.Name) // path in container dst := path.Join(DataContainersPath, do.Name, containerDst) // path on host // TODO: deal with do.Operations.ContainerNumbers ....! // we probably need to update Import logger.Debugf("Container destination =>\t%s\n", containerDst) logger.Debugf("Local destination =>\t\t%s\n", dst) if err = os.MkdirAll(dst, 0700); err != nil { return fmt.Errorf("Error making data directory: %v", err) } // we accept two csvs: one for validators, one for accounts // if there's only one, its for validators and accounts var csvFiles []string var csvPaths string if do.CSV != "" { csvFiles = strings.Split(do.CSV, ",") if len(csvFiles) > 1 { csvPath1 := fmt.Sprintf("%s/%s/%s/%s", ErisContainerRoot, "chains", do.ChainID, "validators.csv") csvPath2 := fmt.Sprintf("%s/%s/%s/%s", ErisContainerRoot, "chains", do.ChainID, "accounts.csv") csvPaths = fmt.Sprintf("%s,%s", csvPath1, csvPath2) } else { csvPaths = fmt.Sprintf("%s/%s/%s/%s", ErisContainerRoot, "chains", do.ChainID, "genesis.csv") } } filesToCopy := []stringPair{ {do.Path, ""}, {do.GenesisFile, "genesis.json"}, {do.ConfigFile, "config.toml"}, {do.Priv, "priv_validator.json"}, } if len(csvFiles) == 1 { filesToCopy = append(filesToCopy, stringPair{csvFiles[0], "genesis.csv"}) } else if len(csvFiles) > 1 { filesToCopy = append(filesToCopy, stringPair{csvFiles[0], "validators.csv"}) filesToCopy = append(filesToCopy, stringPair{csvFiles[1], "accounts.csv"}) } logger.Infof("Copying chain files into the correct location.\n") if err := copyFiles(dst, filesToCopy); err != nil { return err } // copy from host to container logger.Debugf("Copying Files into DataCont =>\t%s:%s\n", dst, containerDst) importDo := definitions.NowDo() importDo.Name = do.Name importDo.Operations = do.Operations importDo.Destination = ErisContainerRoot importDo.Source = filepath.Join(DataContainersPath, do.Name) if err = data.ImportData(importDo); err != nil { return err } chain := loaders.MockChainDefinition(do.Name, do.ChainID, false, do.Operations.ContainerNumber) //set maintainer info chain.Maintainer.Name, chain.Maintainer.Email, err = config.GitConfigUser() if err != nil { logger.Debugf(err.Error()) } // write the chain definition file ... fileName := filepath.Join(ChainsPath, do.Name) + ".toml" if _, err = os.Stat(fileName); err != nil { if err = WriteChainDefinitionFile(chain, fileName); err != nil { return fmt.Errorf("error writing chain definition to file: %v", err) } } chain, err = loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } logger.Debugf("Chain Loaded. Image =>\t\t%v\n", chain.Service.Image) chain.Operations.PublishAllPorts = do.Operations.PublishAllPorts // TODO: remove this and marshall into struct from cli directly // cmd should be "new" or "install" chain.Service.Command = cmd // write the list of <key>:<value> config options as flags buf := new(bytes.Buffer) for _, cv := range do.ConfigOpts { spl := strings.Split(cv, "=") if len(spl) != 2 { return fmt.Errorf("Config options should be <key>=<value> pairs. Got %s", cv) } buf.WriteString(fmt.Sprintf(" --%s=%s", spl[0], spl[1])) } configOpts := buf.String() // set chainid and other vars envVars := []string{ fmt.Sprintf("CHAIN_ID=%s", do.ChainID), fmt.Sprintf("CONTAINER_NAME=%s", containerName), fmt.Sprintf("CSV=%v", csvPaths), // for mintgen fmt.Sprintf("CONFIG_OPTS=%s", configOpts), // for config.toml fmt.Sprintf("NODE_ADDR=%s", do.Gateway), // etcb host fmt.Sprintf("DOCKER_FIX=%s", " "), // https://github.com/docker/docker/issues/14203 } envVars = append(envVars, do.Env...) if do.Run { // run erisdb instead of tendermint envVars = append(envVars, "ERISDB_API=true") } logger.Debugf("Set env vars from setupChain =>\t%v\n", envVars) chain.Service.Environment = append(chain.Service.Environment, envVars...) logger.Debugf("Set links from setupChain =>\t%v\n", do.Links) chain.Service.Links = append(chain.Service.Links, do.Links...) // TODO: if do.N > 1 ... chain.Operations.DataContainerName = util.DataContainersName(do.Name, do.Operations.ContainerNumber) if err := bootDependencies(chain, do); err != nil { return err } logger.Debugf("Starting chain via Docker =>\t%s\n", chain.Service.Name) logger.Debugf("\twith Image =>\t\t%s\n", chain.Service.Image) err = perform.DockerRunService(chain.Service, chain.Operations) // this err is caught in the defer above return }
//------------------------------------------------------------------------ func startChain(do *definitions.Do, exec bool) error { chain, err := loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { logger.Errorln("Cannot start a chain I cannot find.") do.Result = "no file" return nil } if chain.Name == "" { logger.Errorln("Cannot start a chain without a name.") do.Result = "no name" return nil } // boot the dependencies (eg. keys) if err := bootDependencies(chain, do); err != nil { return err } chain.Service.Command = loaders.ErisChainStart util.Merge(chain.Operations, do.Operations) chain.Service.Environment = append(chain.Service.Environment, "CHAIN_ID="+chain.ChainID) chain.Service.Environment = append(chain.Service.Environment, do.Env...) if do.Run { chain.Service.Environment = append(chain.Service.Environment, "ERISDB_API=true") } chain.Service.Links = append(chain.Service.Links, do.Links...) logger.Infof("StartChainRaw to DockerRunService =>\t%s\n", chain.Service.Name) logger.Debugf("\twith ChainID =>\t\t%v\n", chain.ChainID) logger.Debugf("\twith Environment =>\t%v\n", chain.Service.Environment) logger.Debugf("\twith AllPortsPublshd =>\t%v\n", chain.Operations.PublishAllPorts) if exec { if do.Image != "" { chain.Service.Image = do.Image } chain.Operations.Args = do.Operations.Args logger.Debugf("\twith Args =>\t\t%v:%v\n", chain.Operations.Args, chain.Operations.Interactive) // This override is necessary because erisdb uses an entryPoint and // the perform package will respect the images entryPoint if it // exists. chain.Service.EntryPoint = "" chain.Service.Command = "" // there is literally never a reason not to randomize the ports. chain.Operations.PublishAllPorts = true // always link the chain to the exec container when doing chains exec // so that there is never any problems with sending info to the service (chain) container chain.Service.Links = append(chain.Service.Links, fmt.Sprintf("%s:%s", util.ContainersName("chain", chain.Name, 1), "chain")) err = perform.DockerExecService(chain.Service, chain.Operations) } else { err = perform.DockerRunService(chain.Service, chain.Operations) } if err != nil { do.Result = "error" return err } return nil }
// XXX: What's going on here? => [csk]: magic func RenameChain(do *definitions.Do) error { if do.Name == do.NewName { return fmt.Errorf("Cannot rename to same name") } newNameBase := strings.Replace(do.NewName, filepath.Ext(do.NewName), "", 1) transformOnly := newNameBase == do.Name if isKnownChain(do.Name) { logger.Infof("Renaming chain =>\t\t%s:%s\n", do.Name, do.NewName) logger.Debugf("Loading Chain Def File =>\t%s\n", do.Name) chainDef, err := loaders.LoadChainDefinition(do.Name, false, 1) // TODO:CNUM if err != nil { return err } if !transformOnly { logger.Debugln("Embarking on DockerRename.") err = perform.DockerRename(chainDef.Service, chainDef.Operations, do.Name, newNameBase) if err != nil { return err } } oldFile := util.GetFileByNameAndType("chains", do.Name) if err != nil { return err } if filepath.Base(oldFile) == do.NewName { logger.Infoln("Those are the same file. Not renaming") return nil } logger.Debugln("Renaming Chain Definition File.") var newFile string if filepath.Ext(do.NewName) == "" { newFile = strings.Replace(oldFile, do.Name, do.NewName, 1) } else { newFile = filepath.Join(BlockchainsPath, do.NewName) } chainDef.Name = newNameBase chainDef.Service.Name = "" chainDef.Service.Image = "" err = WriteChainDefinitionFile(chainDef, newFile) if err != nil { return err } if !transformOnly { logger.Infof("Renaming DataC (fm ChainRaw) =>\t%s:%s\n", do.Name, do.NewName) do.Operations.ContainerNumber = chainDef.Operations.ContainerNumber logger.Debugf("\twith ContainerNumber =>\t%d\n", do.Operations.ContainerNumber) err = data.RenameData(do) if err != nil { return err } } os.Remove(oldFile) } else { return fmt.Errorf("I cannot find that chain. Please check the chain name you sent me.") } return nil }
// the main function for setting up a chain container // handles both "new" and "fetch" - most of the differentiating logic is in the container func setupChain(do *definitions.Do, cmd string) (err error) { // XXX: if do.Name is unique, we can safely assume (and we probably should) that do.Operations.ContainerNumber = 1 // do.Name is mandatory if do.Name == "" { return fmt.Errorf("setupChain requires a chainame") } containerName := util.ChainContainersName(do.Name, do.Operations.ContainerNumber) if do.ChainID == "" { do.ChainID = do.Name } // do.Run containers and exit (creates data container) if !data.IsKnown(containerName) { if err := perform.DockerCreateDataContainer(do.Name, do.Operations.ContainerNumber); err != nil { return fmt.Errorf("Error creating data containr =>\t%v", err) } } logger.Debugf("Chain's Data Contain Built =>\t%s\n", do.Name) // if something goes wrong, cleanup defer func() { if err != nil { logger.Infof("Error on setupChain =>\t\t%v\n", err) logger.Infoln("Cleaning up...") if err2 := RmChain(do); err2 != nil { err = fmt.Errorf("Tragic! Our marmots encountered an error during setupChain for %s.\nThey also failed to cleanup after themselves (remove containers) due to another error.\nFirst error =>\t\t\t%v\nCleanup error =>\t\t%v\n", containerName, err, err2) } } }() // copy do.Path, do.GenesisFile, config into container containerDst := path.Join("blockchains", do.Name) // path in container dst := path.Join(DataContainersPath, do.Name, containerDst) // path on host // TODO: deal with do.Operations.ContainerNumbers ....! // we probably need to update Import if err = os.MkdirAll(dst, 0700); err != nil { return fmt.Errorf("Error making data directory: %v", err) } if do.Path != "" { if err = Copy(do.Path, dst); err != nil { return err } } if do.GenesisFile != "" { if err = Copy(do.GenesisFile, path.Join(dst, "genesis.json")); err != nil { return err } } else { // TODO: do.Run mintgen and open the do.GenesisFile in editor } if do.ConfigFile != "" { if err = Copy(do.ConfigFile, path.Join(dst, "config."+path.Ext(do.ConfigFile))); err != nil { return err } } // copy from host to container logger.Debugf("Copying Files into DataCont =>\t%s:%s\n", dst, containerDst) importDo := definitions.NowDo() importDo.Name = do.Name importDo.Operations = do.Operations if err = data.ImportData(importDo); err != nil { return err } chain := loaders.MockChainDefinition(do.Name, do.ChainID, false, do.Operations.ContainerNumber) //get maintainer info uName, err := gitconfig.Username() if err != nil { logger.Debugf("Could not find git user.name, setting chain.Maintainer.Name = \"\"") uName = "" } email, err := gitconfig.Email() if err != nil { logger.Debugf("Could not find git user.email, setting chain.Maintainer.Email = \"\"") email = "" } chain.Maintainer.Name = uName chain.Maintainer.Email = email // write the chain definition file ... fileName := filepath.Join(BlockchainsPath, do.Name) + ".toml" if _, err = os.Stat(fileName); err != nil { if err = WriteChainDefinitionFile(chain, fileName); err != nil { return fmt.Errorf("error writing chain definition to file: %v", err) } } chain, err = loaders.LoadChainDefinition(do.Name, false, do.Operations.ContainerNumber) if err != nil { return err } logger.Debugf("Chain Loaded. Image =>\t\t%v\n", chain.Service.Image) chain.Operations.PublishAllPorts = do.Operations.PublishAllPorts // TODO: remove this and marshall into struct from cli directly // cmd should be "new" or "install" chain.Service.Command = cmd // do we need to create our own do.GenesisFile? var genGen bool if do.GenesisFile == "" { genGen = true } // set chainid and other vars envVars := []string{ "CHAIN_ID=" + do.ChainID, "CONTAINER_NAME=" + containerName, fmt.Sprintf("RUN=%v", do.Run), fmt.Sprintf("GENERATE_GENESIS=%v", genGen), } logger.Debugf("Set env vars from setupChain =>\t%v\n", envVars) for _, eV := range envVars { chain.Service.Environment = append(chain.Service.Environment, eV) } // TODO mint vs. erisdb (in terms of rpc) chain.Operations.DataContainerName = util.DataContainersName(do.Name, do.Operations.ContainerNumber) if os.Getenv("TEST_IN_CIRCLE") != "true" { chain.Operations.Remove = true } logger.Debugf("Starting chain via Docker =>\t%s\n", chain.Service.Name) logger.Debugf("\twith Image =>\t\t%s\n", chain.Service.Image) err = perform.DockerRun(chain.Service, chain.Operations) // this err is caught in the defer above return }
// XXX: What's going on here? => [csk]: magic func RenameChain(do *definitions.Do) error { if do.Name == do.NewName { return fmt.Errorf("Cannot rename to same name") } newNameBase := strings.Replace(do.NewName, filepath.Ext(do.NewName), "", 1) transformOnly := newNameBase == do.Name if util.IsKnownChain(do.Name) { log.WithFields(log.Fields{ "from": do.Name, "to": do.NewName, }).Info("Renaming chain") log.WithField("=>", do.Name).Debug("Loading chain definition file") chainDef, err := loaders.LoadChainDefinition(do.Name, false, 1) // TODO:CNUM if err != nil { return err } if !transformOnly { log.Debug("Renaming chain container") err = perform.DockerRename(chainDef.Operations, do.NewName) if err != nil { return err } } oldFile := util.GetFileByNameAndType("chains", do.Name) if err != nil { return err } if filepath.Base(oldFile) == do.NewName { log.Info("Those are the same file. Not renaming") return nil } log.Debug("Renaming chain definition file") var newFile string if filepath.Ext(do.NewName) == "" { newFile = strings.Replace(oldFile, do.Name, do.NewName, 1) } else { newFile = filepath.Join(ChainsPath, do.NewName) } chainDef.Name = newNameBase // Generally we won't want to use Service.Name // as it will be confused with the Name. chainDef.Service.Name = "" // Service.Image should be taken from the default.toml. chainDef.Service.Image = "" err = WriteChainDefinitionFile(chainDef, newFile) if err != nil { return err } if !transformOnly { log.WithFields(log.Fields{ "from": fmt.Sprintf("%s:%d", do.Name, chainDef.Operations.ContainerNumber), "to": fmt.Sprintf("%s:%d", do.NewName, chainDef.Operations.ContainerNumber), }).Info("Renaming chain data container") do.Operations.ContainerNumber = chainDef.Operations.ContainerNumber err = data.RenameData(do) if err != nil { return err } } os.Remove(oldFile) } else { return fmt.Errorf("I cannot find that chain. Please check the chain name you sent me.") } return nil }