func TestModifyResources(t *testing.T) { assert := assert.New(t) version := "1.2.3-test" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(version) pair.Prior.Deployment.Resources["memory"] = "100" pair.Post.Deployment.SourceID.Version = semv.MustParse(version) pair.Post.Deployment.Resources["memory"] = "500" pair.Post.BuildArtifact.Name = "1.2.3" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Created, 0) if assert.Len(client.Deployed, 1) { assert.Regexp("1.2.3", client.Deployed[0].ImageName) assert.Regexp("500", client.Deployed[0].Res["memory"]) } }
func TestModifyImage(t *testing.T) { assert := assert.New(t) sous.Log.Warn.SetOutput(os.Stderr) Log.Debug.SetOutput(os.Stderr) before := "1.2.3-test" after := "2.3.4-new" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(before) pair.Post.Deployment.SourceID.Version = semv.MustParse(after) pair.Post.BuildArtifact.Name = "2.3.4" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } assert.Len(client.Created, 0) if assert.Len(client.Deployed, 1) { assert.Regexp("2.3.4", client.Deployed[0].ImageName) } }
func makeTestState() *sous.State { cluster1 := &sous.Cluster{ Name: "cluster-1", Kind: "singularity", BaseURL: "http://nothing.here.one", Env: sous.EnvDefaults{ "CLUSTER_LONG_NAME": sous.Var("Cluster One"), }, } cluster2 := &sous.Cluster{ Name: "cluster-2", Kind: "singularity", BaseURL: "http://nothing.here.two", Env: sous.EnvDefaults{ "CLUSTER_LONG_NAME": sous.Var("Cluster Two"), }, } return &sous.State{ Defs: sous.Defs{ DockerRepo: "some.docker.repo", Clusters: sous.Clusters{ "cluster-1": cluster1, "cluster-2": cluster2, }, }, Manifests: sous.NewManifests( &sous.Manifest{ Source: project1, Owners: []string{"owner1"}, Kind: sous.ManifestKindService, Deployments: sous.DeploySpecs{ "cluster-1": { Version: semv.MustParse("1.0.0"), DeployConfig: sous.DeployConfig{ Metadata: sous.Metadata{ "BuildBranch": "master", "DeployOn": "build success", }, NumInstances: 2, }, }, "cluster-2": { Version: semv.MustParse("2.0.0"), DeployConfig: sous.DeployConfig{ Metadata: sous.Metadata{ "BuildBranch": "master", "DeployOn": "version advance", }, NumInstances: 3, }, }, }, }, ), } }
func manifest(nc sous.Registry, drepo, containerDir, sourceURL, version string) *sous.Manifest { in := BuildImageName(drepo, version) BuildAndPushContainer(containerDir, in) nc.GetSourceID(docker.NewBuildArtifact(in, nil)) return &sous.Manifest{ Source: sous.SourceLocation{ Repo: sourceURL, }, Owners: []string{`xyz`}, Kind: sous.ManifestKindService, Deployments: sous.DeploySpecs{ "test-cluster": sous.DeploySpec{ DeployConfig: sous.DeployConfig{ Resources: sous.Resources{"cpus": "0.1", "memory": "100", "ports": "1"}, Args: []string{}, Env: sous.Env{"repo": drepo}, //map[s]s NumInstances: 1, Volumes: sous.Volumes{{"/tmp", "/tmp", sous.VolumeMode("RO")}}, }, Version: semv.MustParse(version), }, }, } }
func TestBuildDeployment(t *testing.T) { assert := assert.New(t) m := &Manifest{ Source: SourceLocation{}, Owners: []string{"*****@*****.**"}, Kind: ManifestKindService, } sp := DeploySpec{ DeployConfig: DeployConfig{ Resources: Resources{}, Args: []string{}, Env: Env{}, NumInstances: 3, Volumes: Volumes{ &Volume{"h", "c", "RO"}, }, }, Version: semv.MustParse("1.2.3"), clusterName: "cluster.name", } var ih []DeploySpec nick := "cn" state := &State{Defs: Defs{Clusters: Clusters{nick: &Cluster{BaseURL: "http://not"}}}} d, err := BuildDeployment(state, m, nick, sp, ih) if assert.NoError(err) { if assert.Len(d.DeployConfig.Volumes, 1) { assert.Equal("c", d.DeployConfig.Volumes[0].Container) } assert.Equal(nick, d.ClusterName) } }
func nearestVersion(tags []Tag) semv.Version { for _, t := range tags { v, err := semv.Parse(t.Name) if err == nil { return v } } return semv.MustParse("0.0.0-unversioned") }
func TestModify(t *testing.T) { Log.Debug.SetOutput(os.Stderr) defer Log.Debug.SetOutput(ioutil.Discard) assert := assert.New(t) before := "1.2.3-test" after := "2.3.4-new" pair := baseDeployablePair() pair.Prior.Deployment.SourceID.Version = semv.MustParse(before) pair.Prior.Deployment.DeployConfig.NumInstances = 1 pair.Prior.Deployment.DeployConfig.Volumes = sous.Volumes{{"host", "container", "RO"}} pair.Post.Deployment.SourceID.Version = semv.MustParse(after) pair.Post.Deployment.DeployConfig.NumInstances = 24 pair.Post.Deployment.DeployConfig.Volumes = sous.Volumes{{"host", "container", "RW"}} pair.Post.BuildArtifact.Name = "2.3.4" mods := make(chan *sous.DeployablePair, 1) errs := make(chan error, 10) client := sous.NewDummyRectificationClient() deployer := NewDeployer(client) mods <- pair close(mods) deployer.RectifyModifies(mods, errs) close(errs) for e := range errs { t.Error(e) } if assert.Len(client.Created, 1) { assert.Equal(24, client.Created[0].Count) } if assert.Len(client.Deployed, 1) { assert.Regexp("2.3.4", client.Deployed[0].ImageName) log.Print(client.Deployed[0].Vols) assert.Equal("RW", string(client.Deployed[0].Vols[0].Mode)) } }
func prepareCommand(t *testing.T, cl []string) (*CLI, *cmdr.PreparedExecution, fmt.Stringer, fmt.Stringer) { require := require.New(t) stdin := &bytes.Buffer{} stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} s := &Sous{Version: semv.MustParse(`1.2.3`)} c, err := NewSousCLI(s, stdin, stdout, stderr) require.NoError(err) exe, err := c.Prepare(cl) require.NoError(err) return c, exe, stdout, stderr }
func TestInvokeWithUnknownFlags(t *testing.T) { log.SetFlags(log.Flags() | log.Lshortfile) assert := assert.New(t) require := require.New(t) stdin := &bytes.Buffer{} stdout := &bytes.Buffer{} stderr := &bytes.Buffer{} s := &Sous{Version: semv.MustParse(`1.2.3`)} c, err := NewSousCLI(s, stdin, stdout, stderr) require.NoError(err) c.Invoke([]string{`sous`, `-cobblers`}) assert.Regexp(`flag provided but not defined`, stderr.String()) }
// Version returns the SourceID. func (sc *SourceContext) Version() SourceID { v, err := semv.Parse(sc.NearestTagName) if err != nil { v = nearestVersion(sc.Tags) } // Append revision ID. v = semv.MustParse(v.Format("M.m.p-?") + "+" + sc.Revision) sv := SourceID{ Location: SourceLocation{ Repo: sc.RemoteURL, Dir: sc.OffsetDir, }, Version: v, } Log.Debug.Printf("Version: % #v", sv) return sv }
// NewTerminal creates a new test terminal. func NewTerminal(t *testing.T, vstr string) *Terminal { v := semv.MustParse(vstr) baseout := TestOutput{"stdout", &bytes.Buffer{}, t} baseerr := TestOutput{"stderr", &bytes.Buffer{}, t} combined := TestOutput{"combined output", &bytes.Buffer{}, t} in := &bytes.Buffer{} out := io.MultiWriter(baseout.Buffer, combined.Buffer) err := io.MultiWriter(baseerr.Buffer, combined.Buffer) s := &cli.Sous{Version: v} c, er := cli.NewSousCLI(s, in, out, err) if er != nil { panic(er) } return &Terminal{c, baseout, baseerr, combined, []string{}, t} }
func buildManifest(cluster, repo, version string) *sous.Manifest { return &sous.Manifest{ Owners: []string{"tom", "dick", "harry"}, Source: sous.SourceLocation{Repo: repo}, Kind: sous.ManifestKindService, Deployments: sous.DeploySpecs{ cluster: sous.DeploySpec{ Version: semv.MustParse(version), DeployConfig: sous.DeployConfig{ Resources: sous.Resources{ "cpus": "1", "memory": "256", "ports": "1", }, }, }, }, } }
func TestHTTPNameInserter(t *testing.T) { reqd := false h := func(rw http.ResponseWriter, r *http.Request) { if meth := r.Method; strings.ToUpper(meth) != "PUT" { t.Errorf("Method should be PUT was: %s", meth) } if path := r.URL.Path; path != "/artifact" { t.Errorf("Path should be '/artifact' but was: %s", path) } body, err := ioutil.ReadAll(r.Body) if err != nil { t.Error(err) } if len(body) <= 0 { t.Errorf("Empty body") } rw.WriteHeader(200) reqd = true } srv := httptest.NewServer(http.HandlerFunc(h)) hni, err := NewHTTPNameInserter(srv.URL) if err != nil { t.Error(err) } err = hni.Insert( SourceID{Location: SourceLocation{Repo: "a-repo", Dir: "offset"}, Version: semv.MustParse("5.5.5")}, "dockerthin.com/repo/latest", "", []Quality{}, ) if err != nil { t.Error(err) } if !reqd { t.Errorf("No request issued") } }
func TestState_Validate(t *testing.T) { mid := MustParseManifestID("github.com/user/repo") // TODO: Expand the definition of "valid". At the time of initial writing, // only the individual manifests are validated without reference to // definitions in State.Defs; they should additionally be validated against // these definitions. validState := &State{ Manifests: NewManifestsFromMap(map[ManifestID]*Manifest{ mid: &Manifest{ Source: mid.Source, Kind: ManifestKindService, Deployments: DeploySpecs{ "some-cluster": DeploySpec{ DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1", "memory": "256", "ports": "1", }, NumInstances: 3, }, Version: semv.MustParse("1"), }, }, }, }), } flaws := validState.Validate() if len(flaws) != 0 { for _, f := range flaws { t.Error(f) } t.Fatalf("got %d flaws; want 0", len(flaws)) } }
func (nc *NameCache) dbQueryAllSourceIds() (ids []sous.SourceID, err error) { rows, err := nc.DB.Query("select docker_search_location.repo, " + "docker_search_location.offset, " + "docker_search_metadata.version " + "from " + "docker_search_location natural join docker_search_metadata") if err != nil { return } for rows.Next() { var r, o, v string rows.Scan(&r, &o, &v) ids = append(ids, sous.SourceID{ Location: sous.SourceLocation{ Repo: r, Dir: o, }, Version: semv.MustParse(v), }) } err = rows.Err() return }
RepairError: "unable to repair invalid ManifestKind", }, { OriginalManifest: &Manifest{ Kind: ManifestKindService, Deployments: DeploySpecs{ "some-cluster": DeploySpec{ DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1", // NOTE: Missing memory. "ports": "1", }, NumInstances: 3, }, Version: semv.MustParse("1"), }, }, }, FixedManifest: &Manifest{ Kind: ManifestKindService, Deployments: DeploySpecs{ "some-cluster": DeploySpec{ DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1", // NOTE: Memory repaired by setting to default. "memory": "100", "ports": "1", }, NumInstances: 3,
package sous import ( "testing" "github.com/samsalisbury/semv" ) var parseSourceIDTests = map[string]SourceID{ "github.com/opentable/sous,1,": { Location: SourceLocation{ Repo: "github.com/opentable/sous", }, Version: semv.MustParse("1"), }, ":github.com/opentable/sous:1:": { Location: SourceLocation{ Repo: "github.com/opentable/sous", }, Version: semv.MustParse("1"), }, "github.com/opentable/sous,1": { Location: SourceLocation{ Repo: "github.com/opentable/sous", }, Version: semv.MustParse("1"), }, ",github.com/opentable/sous,1,util": { Location: SourceLocation{ Repo: "github.com/opentable/sous", Dir: "util",
package main import ( "runtime" "github.com/samsalisbury/semv" ) // VersionString is the version of Sous. const VersionString = "0.1.1-beta.1" var ( // Version is the version of Sous. Version = semv.MustParse(VersionString + "+" + Revision) // OS is the OS this Sous is running on. OS = runtime.GOOS // Arch is the architecture this Sous is running on. Arch = runtime.GOARCH // GoVersion is the version of Go this sous was built with. GoVersion = runtime.Version() // Revision may be set by the build process using build flags. Revision string )
}, Env: Env{ "ENV_2": "ENV TWO FLAVORED", }, NumInstances: 5, }, }, }, }, ), } } var expectedDeployments = NewDeployments( &Deployment{ SourceID: project1.SourceID(semv.MustParse("1.0.0")), ClusterName: "cluster-1", Cluster: cluster1, Kind: ManifestKindService, Owners: NewOwnerSet("owner1"), DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1", "mem": "1024", }, Env: Env{ "ALL": "IS ONE", "ENV_1": "ENV ONE", "CLUSTER_LONG_NAME": "Cluster One", }, Metadata: Metadata{
func makeTestState() *State { return &State{ Defs: Defs{ DockerRepo: "some.docker.repo", Clusters: Clusters{ "cluster-1": cluster1, "cluster-2": cluster2, }, EnvVars: EnvDefs{ { Name: "CLUSTER_LONG_NAME", Desc: "The human-friendly name of this cluster.", Scope: "cluster", Type: VarType("string"), }, }, Resources: FieldDefinitions{ {Name: "cpus", Type: "float"}, {Name: "mem", Type: "memory_size"}, }, }, Manifests: NewManifests( &Manifest{ Source: project1, Owners: []string{"owner1"}, Kind: ManifestKindService, Deployments: DeploySpecs{ "cluster-1": { Version: semv.MustParse("1.0.0"), DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1", "mem": "1024", }, Env: Env{ "ALL": "IS ONE", "ENV_1": "ENV ONE", }, Metadata: Metadata{ "everybody": "wants to be a cat", "name": "O'Malley", }, NumInstances: 2, }, }, "cluster-2": { Version: semv.MustParse("2.0.0"), DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "2", "mem": "2048", }, Env: Env{ "ALL": "IS ONE", "ENV_2": "ENV TWO", }, Metadata: Metadata{ "everybody": "wants to be a cat", "name": "Duchess", }, NumInstances: 3, }, }, }, }, &Manifest{ Source: project1, Flavor: "some-flavor", Owners: []string{"owner1flav"}, Kind: ManifestKindService, Deployments: DeploySpecs{ "cluster-1": { Version: semv.MustParse("1.0.1"), DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "1.5", "mem": "1024", }, Env: Env{ "ENV_1": "ENV ONE FLAVORED", }, NumInstances: 4, }, }, "cluster-2": { Version: semv.MustParse("2.0.1"), DeployConfig: DeployConfig{ Resources: Resources{ "cpus": "2.5", "mem": "2048", }, Env: Env{ "ENV_2": "ENV TWO FLAVORED", }, NumInstances: 5, }, }, }, }, ), } }
func TestWriteState(t *testing.T) { steadyManifest := buildManifest("test-cluster", "github.com/opentable/steady", "1.2.3") diesManifest := buildManifest("test-cluster", "github.com/opentable/dies", "133.56.987431") changesManifest := buildManifest("test-cluster", "github.com/opentable/changes", "0.17.19") newManifest := buildManifest("test-cluster", "github.com/opentable/new", "0.0.1") state := &sous.State{} state.Defs.Clusters = make(sous.Clusters) state.Defs.Clusters["test-cluster"] = &sous.Cluster{Name: "test-cluster"} // Current issue: "incomplete" manifests never complete to get updates // There aren't any deploy specs for extra, which mimics this bug state.Defs.Clusters["extra-cluster"] = &sous.Cluster{Name: "cluster-cluster"} state.Manifests = sous.NewManifests() state.Manifests.Add(steadyManifest) state.Manifests.Add(diesManifest) state.Manifests.Add(changesManifest) sm := sous.DummyStateManager{State: state} smm, err := sm.ReadState() if err != nil { t.Fatal("State manager double is broken", err) } if smm.Manifests.Len() <= 0 { t.Fatal("State manager double is empty") } gf := func() server.Injector { di := psyringe.New() //di.Add(sous.NewLogSet(os.Stderr, os.Stderr, ioutil.Discard)) di.Add(sous.NewLogSet(os.Stderr, ioutil.Discard, ioutil.Discard)) graph.AddInternals(di) di.Add( func() graph.StateReader { return graph.StateReader{StateReader: &sm} }, func() graph.StateWriter { return graph.StateWriter{StateWriter: &sm} }, ) di.Add(&config.Verbosity{}) return di } testServer := httptest.NewServer(server.SousRouteMap.BuildRouter(gf)) defer testServer.Close() hsm, err := sous.NewHTTPStateManager(testServer.URL) if err != nil { t.Fatal(err) } originalState, err := hsm.ReadState() if err != nil { t.Fatal(err) } for id, m := range originalState.Manifests.Snapshot() { t.Logf("hsm INITIAL state: Manifest %q; Kind = %q\n %#v\n", id, m.Kind, m) } log.Printf("original state: %#v", originalState) if originalState.Manifests.Len() != state.Manifests.Len() { t.Errorf("Local state has %d manifests to remote's %d", originalState.Manifests.Len(), state.Manifests.Len()) } originalState.Manifests.Remove(diesManifest.ID()) originalState.Manifests.Add(newManifest) ch, there := originalState.Manifests.Get(changesManifest.ID()) if !there { t.Fatalf("Changed manifest %q not in local manifests!", changesManifest.ID()) } changedDeployment := ch.Deployments["test-cluster"] changedDeployment.Version = semv.MustParse("0.18.0") ch.Deployments["test-cluster"] = changedDeployment originalState.Manifests.Set(ch.ID(), ch) log.Printf("state after update: %#v", originalState) if err := hsm.WriteState(originalState); err != nil { t.Fatalf("Failed to write state: %+v", err) } state, err = hsm.ReadState() if err != nil { t.Fatal(err) } for id, m := range state.Manifests.Snapshot() { t.Logf("hsm UPDATED state: Manifest %q; Kind = %q\n %#v\n", id, m.Kind, m) } if originalState.Manifests.Len() != state.Manifests.Len() { t.Errorf("After write, local state has %d manifests to remote's %d", originalState.Manifests.Len(), state.Manifests.Len()) } d, there := state.Manifests.Get(diesManifest.ID()) if there { t.Errorf("Removed manifest still in server's state: %#v", d) } _, there = state.Manifests.Get(steadyManifest.ID()) if !there { t.Errorf("Untouched manifest not in server's state") } _, there = state.Manifests.Get(newManifest.ID()) if !there { t.Errorf("Added manifest not in server's state") } c, there := state.Manifests.Get(changesManifest.ID()) if !there { t.Errorf("Changed manifest missing from server's state") } expectedVersion := "0.18.0" actualVersion := c.Deployments["test-cluster"].Version.String() if actualVersion != expectedVersion { t.Errorf("Server's version of changed state was %q; want %q", actualVersion, expectedVersion) } }
func TestRealDiffConcentration(t *testing.T) { log.SetFlags(log.Flags() | log.Lshortfile) assert := assert.New(t) require := require.New(t) intended := NewDeployments() existing := NewDeployments() defs := Defs{Clusters: map[string]*Cluster{"test": &Cluster{}}} makeDepl := func(repo, verstr string, num int) *Deployment { version := semv.MustParse(verstr) cl := defs.Clusters["test"] owners := OwnerSet{} owners.Add("judson") return &Deployment{ SourceID: SourceID{ Location: SourceLocation{ Repo: repo, }, Version: version, }, Cluster: cl, ClusterName: "test", DeployConfig: DeployConfig{ NumInstances: num, Env: map[string]string{}, Resources: map[string]string{ "cpu": ".1", "memory": "100", "ports": "1", }, }, Owners: owners, } } repoOne := "github.com/opentable/one" repoTwo := "github.com/opentable/two" repoThree := "github.com/opentable/three" repoFour := "github.com/opentable/four" repoFive := "github.com/opentable/five" existing.MustAdd(makeDepl(repoOne, "111.1.1", 1)) //remove //intended: gone existing.MustAdd(makeDepl(repoTwo, "1.0.0", 1)) //same intended.MustAdd(makeDepl(repoTwo, "1.0.0", 1)) //same // existing: doesn't yet intended.MustAdd(makeDepl(repoFour, "1.0.0", 1)) //create existing.MustAdd(makeDepl(repoThree, "1.0.0", 1)) //changed intended.MustAdd(makeDepl(repoThree, "1.0.0", 2)) //changed existing.MustAdd(makeDepl(repoFive, "1.0.0", 1)) //changed intended.MustAdd(makeDepl(repoFive, "2.0.0", 1)) //changed dc := existing.Diff(intended).Concentrate(defs) ds, err := dc.collect() require.NoError(err) if assert.Len(ds.Gone.Snapshot(), 1, "Should have one deleted item.") { it, _ := ds.Gone.Any(func(*Manifest) bool { return true }) assert.Equal(string(it.Source.Repo), repoOne) } if assert.Len(ds.Same.Snapshot(), 1, "Should have one unchanged item.") { it, _ := ds.Same.Any(func(*Manifest) bool { return true }) assert.Equal(string(it.Source.Repo), repoTwo) } if assert.Len(ds.Changed, 2, "Should have two modified items.") { chNum, chVer := ds.Changed[0], ds.Changed[1] if repoThree == chVer.name.Source.Repo { chNum, chVer = chVer, chNum } assert.Equal(repoThree, string(chNum.name.Source.Repo)) assert.Equal(repoThree, string(chNum.Prior.Source.Repo)) assert.Equal(repoThree, string(chNum.Post.Source.Repo)) log.Printf("%+v", chNum) log.Printf("%+v", chNum.Prior) log.Printf("%+v", chNum.Post) assert.Equal(chNum.Prior.Deployments["test"].NumInstances, 1) assert.Equal(chNum.Post.Deployments["test"].NumInstances, 2) assert.Equal(repoFive, string(chVer.name.Source.Repo)) assert.Equal(repoFive, string(chVer.Prior.Source.Repo)) assert.Equal(repoFive, string(chVer.Post.Source.Repo)) ver1 := semv.MustParse("1.0.0") ver2 := semv.MustParse("2.0.0") assert.Equal(ver1, chVer.Prior.Deployments["test"].Version) assert.Equal(ver2, chVer.Post.Deployments["test"].Version) } if assert.Equal(ds.New.Len(), 1, "Should have one added item.") { it, _ := ds.New.Any(func(*Manifest) bool { return true }) assert.Equal(string(it.Source.Repo), repoFour) } }
func exampleState() *sous.State { sl := sous.SourceLocation{ Repo: "github.com/opentable/sous", } sl2 := sous.SourceLocation{ Repo: "github.com/user/project", } return &sous.State{ Manifests: sous.NewManifests( &sous.Manifest{ Source: sl, Owners: []string{"Judson", "Sam"}, Kind: "http-service", Deployments: map[string]sous.DeploySpec{ "cluster-1": sous.DeploySpec{ DeployConfig: sous.DeployConfig{ Env: sous.Env{ "SOME_DB_URL": "https://some.database", }, Resources: sous.Resources{ "cpus": "0.1", "memory": "2GB", "ports": "1", }, NumInstances: 6, Volumes: sous.Volumes{}, }, Version: semv.MustParse("1.0.0-rc.1+deadbeef"), }, }, }, &sous.Manifest{ Source: sl2, Owners: []string{"Sous Team"}, Kind: "http-service", Deployments: map[string]sous.DeploySpec{ "other-cluster": { DeployConfig: sous.DeployConfig{ Env: sous.Env{ "DEBUG": "YES", }, Resources: sous.Resources{ "cpus": "1", "memory": "256MB", "ports": "1", }, Volumes: sous.Volumes{}, }, Version: semv.MustParse("0.3.1-beta+b4d455ee"), }, }, }, ), Defs: sous.Defs{ DockerRepo: "docker.somewhere.horse", Clusters: sous.Clusters{ "cluster-1": &sous.Cluster{ Kind: "singularity", BaseURL: "http://singularity.example.com", }, "other-cluster": &sous.Cluster{ Kind: "singularity", BaseURL: "http://some.singularity.cluster", }, }, EnvVars: sous.EnvDefs{}, Resources: sous.FieldDefinitions{}, Metadata: sous.FieldDefinitions{}, }, } }