// eris chains new -c _ -csv _ func TestChainsNewConfigAndCSV(t *testing.T) { chainID := "testChainsNewConfigAndCSV" do := def.NowDo() do.Name = chainID do.ConfigFile = filepath.Join(common.ChainsPath, "default", "config.toml") do.CSV = filepath.Join(common.ChainsPath, "default", "genesis.csv") do.Operations.ContainerNumber = 1 do.Operations.PublishAllPorts = true log.WithField("=>", do.Name).Info("Creating chain (from tests)") tests.IfExit(NewChain(do)) _, err := ioutil.ReadFile(do.ConfigFile) if err != nil { tests.IfExit(err) } // remove the data container defer removeChainContainer(t, chainID, do.Operations.ContainerNumber) // verify the contents of config.toml ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) ops.Args = []string{"cat", fmt.Sprintf("/home/eris/.eris/chains/%s/config.toml", chainID)} result := trimResult(string(runContainer(t, ops))) configDefault := filepath.Join(erisDir, "chains", "default", "config.toml") read, err := ioutil.ReadFile(configDefault) if err != nil { tests.IfExit(err) } contents := trimResult(string(read)) if result != contents { tests.IfExit(fmt.Errorf("config not properly copied. Got: %s \n Expected: %s", result, contents)) } // verify the contents of genesis.json (should have the validator from the csv) ops = loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) ops.Args = []string{"cat", fmt.Sprintf("/home/eris/.eris/chains/%s/genesis.json", chainID)} result = string(runContainer(t, ops)) var found bool for _, s := range strings.Split(result, "\n") { if strings.Contains(s, ini.DefaultPubKeys[0]) { found = true break } } if !found { tests.IfExit(fmt.Errorf("Did not find pubkey %s in genesis.json: %s", ini.DefaultPubKeys[0], result)) } }
func TestCreateDataSimple(t *testing.T) { const ( name = "testdata" number = 199 ) if n := util.HowManyContainersExisting(name, def.TypeData); n != 0 { t.Fatalf("expecting 0 containers, got %v", n) } ops := loaders.LoadDataDefinition(name, number) if err := DockerCreateData(ops); err != nil { t.Fatalf("expected data container created, got %v", err) } if n := util.HowManyContainersExisting(name, def.TypeData); n != 1 { t.Fatalf("expecting 1 containers, got %v", n) } // Try to create a duplicate. if err := DockerCreateData(ops); err == nil { t.Fatalf("expected an error, got nil") } tests.RemoveAllContainers() }
// ImportData does what it says. It imports from a host's Source to a Dest // in a data container. It returns an error. // // do.Name - name of the data container to use (required) // do.Operations.ContainerNumber - container number (optional) // do.Source - directory which should be imported (required) // do.Destination - directory to _unload_ the payload into (required) // func ImportData(do *definitions.Do) error { log.WithFields(log.Fields{ "from": do.Source, "to": do.Destination, }).Debug("Importing") if util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { srv := PretendToBeAService(do.Name, do.Operations.ContainerNumber) service, exists := perform.ContainerExists(srv.Operations) if !exists { return fmt.Errorf("There is no data container for that service.") } if err := checkErisContainerRoot(do, "import"); err != nil { return err } containerName := util.DataContainersName(do.Name, do.Operations.ContainerNumber) os.Chdir(do.Source) reader, err := util.Tar(do.Source, 0) if err != nil { return err } defer reader.Close() opts := docker.UploadToContainerOptions{ InputStream: reader, Path: do.Destination, NoOverwriteDirNonDir: true, } log.WithField("=>", containerName).Info("Copying into container") log.WithField("path", do.Source).Debug() if err := util.DockerClient.UploadToContainer(service.ID, opts); err != nil { return err } doChown := definitions.NowDo() doChown.Operations.DataContainerName = containerName doChown.Operations.ContainerType = "data" doChown.Operations.ContainerNumber = do.Operations.ContainerNumber //required b/c `docker cp` (UploadToContainer) goes in as root doChown.Operations.Args = []string{"chown", "--recursive", "eris", do.Destination} _, err = perform.DockerRunData(doChown.Operations, nil) if err != nil { return fmt.Errorf("Error changing owner: %v\n", err) } } else { log.WithField("name", do.Name).Info("Data container does not exist.") ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) if err := perform.DockerCreateData(ops); err != nil { return fmt.Errorf("Error creating data container %v.", err) } return ImportData(do) } do.Result = "success" return nil }
func ImportData(do *definitions.Do) error { if util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { srv := PretendToBeAService(do.Name, do.Operations.ContainerNumber) service, exists := perform.ContainerExists(srv.Operations) if !exists { return fmt.Errorf("There is no data container for that service.") } containerName := util.DataContainersName(do.Name, do.Operations.ContainerNumber) logger.Debugf("Importing FROM =>\t\t%s\n", do.Source) os.Chdir(do.Source) logger.Debugf("Importing TO =>\t\t\t%s\n", do.Destination) reader, err := util.Tar(do.Source, 0) if err != nil { return err } defer reader.Close() opts := docker.UploadToContainerOptions{ InputStream: reader, Path: do.Destination, NoOverwriteDirNonDir: true, } logger.Infof("Copying into Cont. ID =>\t%s\n", service.ID) logger.Debugf("\tPath =>\t\t\t%s\n", do.Source) if err := util.DockerClient.UploadToContainer(service.ID, opts); err != nil { return err } doChown := definitions.NowDo() doChown.Operations.DataContainerName = containerName doChown.Operations.ContainerType = "data" doChown.Operations.ContainerNumber = 1 doChown.Operations.Args = []string{"chown", "--recursive", "eris", do.Destination} _, err = perform.DockerRunData(doChown.Operations, nil) if err != nil { return fmt.Errorf("Error changing owner: %v\n", err) } } else { ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) if err := perform.DockerCreateData(ops); err != nil { return fmt.Errorf("Error creating data container %v.", err) } return ImportData(do) } do.Result = "success" 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 ExecData(do *definitions.Do) error { if util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { log.WithField("=>", do.Operations.DataContainerName).Info("Executing data container") ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) if err := perform.DockerExecData(ops, nil); err != nil { return err } } else { return fmt.Errorf("The marmots cannot find that data container.\nPlease check the name of the data container with [eris data ls].") } do.Result = "success" return nil }
func RenameData(do *definitions.Do) error { logger.Infof("Renaming Data =>\t\t%s:%s\n", do.Name, do.NewName) logger.Debugf("\twith ContainerNumber =>\t%d\n", do.Operations.ContainerNumber) if util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) err := perform.DockerRename(ops, do.NewName) if err != nil { return err } } else { return fmt.Errorf("I cannot find that data container. Please check the data container name you sent me.") } do.Result = "success" return nil }
func RenameData(do *definitions.Do) error { log.WithFields(log.Fields{ "from": fmt.Sprintf("%s:%d", do.Name, do.Operations.ContainerNumber), "to": fmt.Sprintf("%s:%d", do.NewName, do.Operations.ContainerNumber), }).Info("Renaming data container") if util.IsDataContainer(do.Name, do.Operations.ContainerNumber) { ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) err := perform.DockerRename(ops, do.NewName) if err != nil { return err } } else { return fmt.Errorf("I cannot find that data container. Please check the data container name you sent me.") } do.Result = "success" return nil }
func TestRunDataBadCommandLine(t *testing.T) { const ( name = "testdata" number = 199 ) if n := util.HowManyContainersExisting(name, def.TypeData); n != 0 { t.Fatalf("expecting 0 containers, got %v", n) } ops := loaders.LoadDataDefinition(name, number) if err := DockerCreateData(ops); err != nil { t.Fatalf("expected data container created, got %v", err) } ops.Args = []string{"/bad/command/line"} if _, err := DockerRunData(ops, nil); err == nil { t.Fatalf("expected command line error, got nil") } tests.RemoveAllContainers() }
func TestRunDataSimple(t *testing.T) { const ( name = "testdata" number = 199 ) if n := util.HowManyContainersExisting(name, def.TypeData); n != 0 { t.Fatalf("expecting 0 containers, got %v", n) } ops := loaders.LoadDataDefinition(name, number) if err := DockerCreateData(ops); err != nil { t.Fatalf("expected data container created, got %v", err) } ops.Args = []string{"uptime"} if _, err := DockerRunData(ops, nil); err != nil { t.Fatalf("expected data successfully run, got %v", err) } tests.RemoveAllContainers() }
// eris chains new --options func TestChainsNewConfigOpts(t *testing.T) { // XXX: need to use a different chainID or remove the local tmp/eris/data/chainID dir with each test! chainID := "testChainsNewConfigOpts" do := def.NowDo() do.Name = chainID do.ConfigOpts = []string{"moniker=satoshi", "p2p=1.1.1.1:42", "fast-sync=true"} do.Operations.ContainerNumber = 1 do.Operations.PublishAllPorts = true log.WithField("=>", do.Name).Info("Creating chain (from tests)") tests.IfExit(NewChain(do)) // remove the data container defer removeChainContainer(t, chainID, do.Operations.ContainerNumber) // verify the contents of config.toml ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) ops.Args = []string{"cat", fmt.Sprintf("/home/eris/.eris/chains/%s/config.toml", chainID)} result := string(runContainer(t, ops)) spl := strings.Split(result, "\n") var found bool for _, s := range spl { if ensureTomlValue(t, s, "moniker", "satoshi") { found = true } if ensureTomlValue(t, s, "node_laddr", "1.1.1.1:42") { found = true } if ensureTomlValue(t, s, "fast_sync", "true") { found = true } } if !found { tests.IfExit(fmt.Errorf("failed to find fields: %s", result)) } }
// 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 }
// eris chains new --dir _ -g _ // the default chain_id is my_tests, so should be overwritten func TestChainsNewDirGen(t *testing.T) { chainID := "testChainsNewDirGen" myDir := filepath.Join(common.DataContainersPath, chainID) if err := os.MkdirAll(myDir, 0700); err != nil { tests.IfExit(err) } contents := "this is a file in the directory\n" if err := ioutil.WriteFile(filepath.Join(myDir, "file.file"), []byte(contents), 0664); err != nil { tests.IfExit(err) } do := def.NowDo() do.GenesisFile = filepath.Join(common.ChainsPath, "default", "genesis.json") do.Name = chainID do.Path = myDir do.Operations.ContainerNumber = 1 do.Operations.PublishAllPorts = true log.WithField("=>", do.Name).Info("Creating chain (from tests)") tests.IfExit(NewChain(do)) // remove the data container defer removeChainContainer(t, chainID, do.Operations.ContainerNumber) // verify the contents of file.file - swap config writer with bytes.Buffer // TODO: functions for facilitating this oldWriter := config.GlobalConfig.Writer newWriter := new(bytes.Buffer) config.GlobalConfig.Writer = newWriter ops := loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) ops.Args = []string{"cat", fmt.Sprintf("/home/eris/.eris/file.file")} b, err := perform.DockerRunData(ops, nil) if err != nil { tests.IfExit(err) } config.GlobalConfig.Writer = oldWriter result := trimResult(string(b)) contents = trimResult(contents) if result != contents { tests.IfExit(fmt.Errorf("file not faithfully copied. Got: %s \n Expected: %s", result, contents)) } // verify the chain_id got swapped in the genesis.json // TODO: functions for facilitating this oldWriter = config.GlobalConfig.Writer newWriter = new(bytes.Buffer) config.GlobalConfig.Writer = newWriter ops = loaders.LoadDataDefinition(do.Name, do.Operations.ContainerNumber) util.Merge(ops, do.Operations) ops.Args = []string{"cat", fmt.Sprintf("/home/eris/.eris/chains/%s/genesis.json", chainID)} //, "|", "jq", ".chain_id"} b, err = perform.DockerRunData(ops, nil) if err != nil { tests.IfExit(err) } config.GlobalConfig.Writer = oldWriter result = string(b) s := struct { ChainID string `json:"chain_id"` }{} if err := json.Unmarshal([]byte(result), &s); err != nil { tests.IfExit(err) } if s.ChainID != chainID { tests.IfExit(fmt.Errorf("ChainID mismatch: got %s, expected %s", s.ChainID, chainID)) } }