diff --git a/cmd/manager/deployments.go b/cmd/manager/deployments.go index 0e67bc19d..7b8f5d4ba 100644 --- a/cmd/manager/deployments.go +++ b/cmd/manager/deployments.go @@ -111,12 +111,12 @@ func newManager(c *router.Context) manager.Manager { cfg := c.Config service := repo.NewInmemRepoService() cp := c.CredentialProvider - repoProvider := repo.NewRepoProvider(service, repo.NewGCSRepoProvider(cp), cp) - expander := manager.NewExpander(getServiceURL(cfg.ExpanderURL, cfg.ExpanderName, expanderPort)) + rp := repo.NewRepoProvider(service, repo.NewGCSRepoProvider(cp), cp) + expander := manager.NewExpander(getServiceURL(cfg.ExpanderURL, cfg.ExpanderName, expanderPort), rp) deployer := manager.NewDeployer(getServiceURL(cfg.DeployerURL, cfg.DeployerName, deployerPort)) address := strings.TrimPrefix(getServiceURL(cfg.MongoAddress, cfg.MongoName, cfg.MongoPort), "http://") repository := createRepository(address) - return manager.NewManager(expander, deployer, repository, repoProvider, service, c.CredentialProvider) + return manager.NewManager(expander, deployer, repository, rp, service, c.CredentialProvider) } func createRepository(address string) repository.Repository { @@ -202,9 +202,9 @@ func createDeploymentHandlerFunc(w http.ResponseWriter, r *http.Request, c *rout handler := "manager: create deployment" util.LogHandlerEntry(handler, r) defer r.Body.Close() - t := getTemplate(w, r, handler) - if t != nil { - d, err := c.Manager.CreateDeployment(t) + depReq := getDeploymentRequest(w, r, handler) + if depReq != nil { + d, err := c.Manager.CreateDeployment(depReq) if err != nil { httputil.BadRequest(w, r, err) return nil @@ -212,6 +212,7 @@ func createDeploymentHandlerFunc(w http.ResponseWriter, r *http.Request, c *rout util.LogHandlerExitWithJSON(handler, w, d, http.StatusCreated) } + return nil } @@ -242,9 +243,9 @@ func putDeploymentHandlerFunc(w http.ResponseWriter, r *http.Request, c *router. return err } - t := getTemplate(w, r, handler) - if t != nil { - d, err := c.Manager.PutDeployment(name, t) + depReq := getDeploymentRequest(w, r, handler) + if depReq != nil { + d, err := c.Manager.PutDeployment(name, depReq) if err != nil { httputil.BadRequest(w, r, err) return nil @@ -252,6 +253,7 @@ func putDeploymentHandlerFunc(w http.ResponseWriter, r *http.Request, c *router. util.LogHandlerExitWithJSON(handler, w, d, http.StatusCreated) } + return nil } @@ -289,14 +291,15 @@ func getPathVariable(w http.ResponseWriter, r *http.Request, variable, handler s return unescaped, nil } -func getTemplate(w http.ResponseWriter, r *http.Request, handler string) *common.Template { +func getDeploymentRequest(w http.ResponseWriter, r *http.Request, handler string) *common.DeploymentRequest { util.LogHandlerEntry(handler, r) - t := &common.Template{} - if err := httputil.Decode(w, r, t); err != nil { + depReq := &common.DeploymentRequest{} + if err := httputil.Decode(w, r, depReq); err != nil { httputil.BadRequest(w, r, err) return nil } - return t + + return depReq } func listManifestsHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.Context) error { @@ -348,9 +351,9 @@ func expandHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.Context handler := "manager: expand config" util.LogHandlerEntry(handler, r) defer r.Body.Close() - t := getTemplate(w, r, handler) - if t != nil { - c, err := c.Manager.Expand(t) + depReq := getDeploymentRequest(w, r, handler) + if depReq != nil { + c, err := c.Manager.Expand(depReq) if err != nil { httputil.BadRequest(w, r, err) return nil @@ -358,6 +361,7 @@ func expandHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.Context util.LogHandlerExitWithJSON(handler, w, c, http.StatusCreated) } + return nil } diff --git a/cmd/manager/deployments_test.go b/cmd/manager/deployments_test.go index 84389694f..bd06e3370 100644 --- a/cmd/manager/deployments_test.go +++ b/cmd/manager/deployments_test.go @@ -60,12 +60,12 @@ func TestHealthz(t *testing.T) { func TestCreateDeployments(t *testing.T) { c := stubContext() - tpl := &common.Template{Name: "foo"} + depReq := &common.DeploymentRequest{Name: "foo"} s := httpHarness(c, "POST /deployments", createDeploymentHandlerFunc) defer s.Close() var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(tpl); err != nil { + if err := json.NewEncoder(&b).Encode(depReq); err != nil { t.Fatal(err) } diff --git a/cmd/manager/manager/expander.go b/cmd/manager/manager/expander.go index a8b2d1fa0..97849f6f9 100644 --- a/cmd/manager/manager/expander.go +++ b/cmd/manager/manager/expander.go @@ -17,190 +17,138 @@ limitations under the License. package manager import ( + "github.com/kubernetes/helm/pkg/common" + "github.com/kubernetes/helm/pkg/expansion" + "github.com/kubernetes/helm/pkg/repo" + "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" - - "github.com/ghodss/yaml" - "github.com/kubernetes/helm/pkg/common" ) +/* const ( // TODO (iantw): Align this with a character not allowed to show up in resource names. layoutNodeKeySeparator = "#" ) +*/ -// ExpandedTemplate is the structure returned by the expansion service. -type ExpandedTemplate struct { +// ExpandedConfiguration is the structure returned by the expansion service. +type ExpandedConfiguration struct { Config *common.Configuration `json:"config"` Layout *common.Layout `json:"layout"` } // Expander abstracts interactions with the expander and deployer services. type Expander interface { - ExpandTemplate(t *common.Template) (*ExpandedTemplate, error) -} - -// TODO: Remove mockResolver when type resolver is completely excised -type mockResolver struct { -} - -func (r *mockResolver) ResolveTypes(c *common.Configuration, i []*common.ImportFile) ([]*common.ImportFile, error) { - return nil, nil + ExpandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) } // NewExpander returns a new initialized Expander. -func NewExpander(url string) Expander { - tr := &mockResolver{} - return &expander{url, tr} +func NewExpander(URL string, rp repo.IRepoProvider) Expander { + if rp == nil { + rp = repo.NewRepoProvider(nil, nil, nil) + } + + return &expander{expanderURL: URL, repoProvider: rp} } type expander struct { + repoProvider repo.IRepoProvider expanderURL string - typeResolver TypeResolver -} - -// TypeResolver finds Types in a Configuration which aren't yet reduceable to an import file -// or primitive, and attempts to replace them with a template from a URL. -type TypeResolver interface { - ResolveTypes(config *common.Configuration, imports []*common.ImportFile) ([]*common.ImportFile, error) } func (e *expander) getBaseURL() string { return fmt.Sprintf("%s/expand", e.expanderURL) } -func expanderError(t *common.Template, err error) error { - return fmt.Errorf("cannot expand template named %s (%s):\n%s", t.Name, err, t.Content) -} - -// ExpanderResponse gives back a layout, which has nested structure -// Resource0 -// ResourceDefinition -// Resource0, 0 -// ResourceDefinition -// Resource0, 0, 0 -// ResourceDefinition -// Resource0, 0, 1 -// ResourceDefinition -// Resource0, 1 -// ResourceDefinition -// -// All the leaf nodes in this tree are either primitives or a currently unexpandable type. -// Next we will resolve all the unexpandable types and re-enter expansion, at which point -// all primitives are untouched and returned as root siblings with no children in the -// resulting layout. The previously unexpandable nodes will become sibling root nodes, -// but with children. We want to replace the leaf nodes that were formerly unexpandable -// with their respective newly created trees. -// -// So, do as follows: -// 1) Do a walk of the tree and find each leaf. Check its Type and place a pointer to it -// into a map with the resource name and type as key if it is non-primitive. -// 2) Re-expand the template with the new imports. -// 3) For each root level sibling, check if its name exists in the hash map from (1) -// 4) Replace the Layout of the node in the hash map with the current node if applicable. -// 5) Return to (1) - -// TODO (iantw): There may be a tricky corner case here where a known template could be -// masked by an unknown template, which on the subsequent expansion could allow a collision -// between the name#template key to exist in the layout given a particular choice of naming. -// In practice, it would be nearly impossible to hit, but consider including properties/name/type -// into a hash of sorts to make this robust... -func walkLayout(l *common.Layout, imports []*common.ImportFile, toReplace map[string]*common.LayoutResource) map[string]*common.LayoutResource { - ret := map[string]*common.LayoutResource{} - toVisit := l.Resources - - for len(toVisit) > 0 { - lr := toVisit[0] - nodeKey := lr.Resource.Name + layoutNodeKeySeparator + lr.Resource.Type - if len(lr.Layout.Resources) == 0 && isTemplate(lr.Resource.Type, imports) { - ret[nodeKey] = lr - } else if toReplace[nodeKey] != nil { - toReplace[nodeKey].Resources = lr.Resources - } - toVisit = append(toVisit, lr.Resources...) - toVisit = toVisit[1:] +// ExpandConfiguration expands the supplied configuration and returns +// an expanded configuration. +func (e *expander) ExpandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) { + expConf, err := e.expandConfiguration(conf) + if err != nil { + return nil, fmt.Errorf("cannot expand configuration:%s\n%v\n", err, conf) } - return ret + return expConf, nil } -// isTemplate returns whether a given type is a template. -func isTemplate(t string, imports []*common.ImportFile) bool { - for _, imp := range imports { - if imp.Name == t { - return true +func (e *expander) expandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) { + resources := []*common.Resource{} + layouts := []*common.LayoutResource{} + + // Iterate over all of the resources in the unexpanded configuration + for _, resource := range conf.Resources { + // A primitive layout resource captures only the name and type + layout := &common.LayoutResource{ + Resource: common.Resource{ + Name: resource.Name, Type: resource.Type, + }, } - } - return false -} - -// ExpandTemplate expands the supplied template, and returns a configuration. -// It will also update the imports in the provided template if any were added -// during type resolution. -func (e *expander) ExpandTemplate(t *common.Template) (*ExpandedTemplate, error) { - // We have a fencepost problem here. - // 1. Start by trying to resolve any missing templates - // 2. Expand the configuration using all the of the imports available to us at this point - // 3. Expansion may yield additional templates, so we run the type resolution again - // 4. If type resolution resulted in new imports being available, return to 2. - config := &common.Configuration{} - if err := yaml.Unmarshal([]byte(t.Content), config); err != nil { - e := fmt.Errorf("Unable to unmarshal configuration (%s): %s", err, t.Content) - return nil, e - } + // If the type is not a chart reference, then it must be primitive + if !repo.IsChartReference(resource.Type) { + // Add it to the flat list of exapnded resources + resources = append(resources, resource) - var finalLayout *common.Layout - needResolve := map[string]*common.LayoutResource{} + // Add its layout to the list of layouts at this level + layouts = append(layouts, layout) + continue + } - // Start things off by attempting to resolve the templates in a first pass. - newImp, err := e.typeResolver.ResolveTypes(config, t.Imports) - if err != nil { - e := fmt.Errorf("type resolution failed: %s", err) - return nil, expanderError(t, e) - } + // It is a chart, so go fetch it, decompress it and unpack it + cbr, _, err := e.repoProvider.GetChartByReference(resource.Type) + if err != nil { + return nil, err + } - t.Imports = append(t.Imports, newImp...) + defer cbr.Close() - for { - // Now expand with everything imported. - result, err := e.expandTemplate(t) + // Now, load the charts contents into strings that we can pass to exapnsion + content, err := cbr.LoadContent() if err != nil { - e := fmt.Errorf("template expansion: %s", err) - return nil, expanderError(t, e) + return nil, err } - // Once we set this layout, we're operating on the "needResolve" *LayoutResources, - // which are pointers into the original layout structure. After each expansion we - // lose the templates in the previous expansion, so we have to keep the first one - // around and keep appending to the pointers in it as we get more layers of expansion. - if finalLayout == nil { - finalLayout = result.Layout + // Build a request to the expansion service and call it to do the expansion + svcReq := &expansion.ServiceRequest{ + ChartInvocation: resource, + Chart: content, } - needResolve = walkLayout(result.Layout, t.Imports, needResolve) - newImp, err = e.typeResolver.ResolveTypes(result.Config, t.Imports) + svcResp, err := e.callService(svcReq) if err != nil { - e := fmt.Errorf("type resolution failed: %s", err) - return nil, expanderError(t, e) + return nil, err } - // If the new imports contain nothing, we are done. Everything is fully expanded. - if len(newImp) == 0 { - result.Layout = finalLayout - return result, nil + // Call ourselves recursively with the list of resources returned by expansion + expConf, err := e.expandConfiguration(svcResp) + if err != nil { + return nil, err } - // Update imports with any new imports from type resolution. - t.Imports = append(t.Imports, newImp...) + // Append the reources returned by the recursion to the flat list of resources + resources = append(resources, expConf.Config.Resources...) + + // This was not a primitive resource, so add its properties to the layout + layout.Properties = resource.Properties + + // Now add the all of the layout resources returned by the recursion to the layout + layout.Resources = expConf.Layout.Resources + layouts = append(layouts, layout) } + + // All done with this level, so return the espanded configuration + return &ExpandedConfiguration{ + Config: &common.Configuration{Resources: resources}, + Layout: &common.Layout{Resources: layouts}, + }, nil } -func (e *expander) expandTemplate(t *common.Template) (*ExpandedTemplate, error) { - j, err := json.Marshal(t) +func (e *expander) callService(svcReq *expansion.ServiceRequest) (*common.Configuration, error) { + j, err := json.Marshal(svcReq) if err != nil { return nil, err } @@ -232,37 +180,11 @@ func (e *expander) expandTemplate(t *common.Template) (*ExpandedTemplate, error) return nil, err } - er := &ExpansionResponse{} - if err := json.Unmarshal(body, er); err != nil { + svcResp := &common.Configuration{} + if err := json.Unmarshal(body, svcResp); err != nil { e := fmt.Errorf("cannot unmarshal response body (%s):%s", err, body) return nil, e } - template, err := er.Unmarshal() - if err != nil { - e := fmt.Errorf("cannot unmarshal response yaml (%s):%v", err, er) - return nil, e - } - - return template, nil -} - -// ExpansionResponse describes the results of marshaling an ExpandedTemplate. -type ExpansionResponse struct { - Config string `json:"config"` - Layout string `json:"layout"` -} - -// Unmarshal creates and returns an ExpandedTemplate from an ExpansionResponse. -func (er *ExpansionResponse) Unmarshal() (*ExpandedTemplate, error) { - template := &ExpandedTemplate{} - if err := yaml.Unmarshal([]byte(er.Config), &template.Config); err != nil { - return nil, fmt.Errorf("cannot unmarshal config (%s):\n%s", err, er.Config) - } - - if err := yaml.Unmarshal([]byte(er.Layout), &template.Layout); err != nil { - return nil, fmt.Errorf("cannot unmarshal layout (%s):\n%s", err, er.Layout) - } - - return template, nil + return svcResp, nil } diff --git a/cmd/manager/manager/expander_test.go b/cmd/manager/manager/expander_test.go index f374c0176..4dd9dacae 100644 --- a/cmd/manager/manager/expander_test.go +++ b/cmd/manager/manager/expander_test.go @@ -26,44 +26,28 @@ import ( "strings" "testing" + "github.com/ghodss/yaml" + "github.com/kubernetes/helm/pkg/chart" "github.com/kubernetes/helm/pkg/common" + "github.com/kubernetes/helm/pkg/expansion" + "github.com/kubernetes/helm/pkg/repo" "github.com/kubernetes/helm/pkg/util" - - "github.com/ghodss/yaml" ) -var validTemplateTestCaseData = common.Template{ - Name: "TestTemplate", - Content: string(validContentTestCaseData), - Imports: validImportFilesTestCaseData, -} - -var validContentTestCaseData = []byte(` -imports: -- path: test-type.py -resources: -- name: test - type: test-type.py - properties: - test-property: test-value -`) - -var validImportFilesTestCaseData = []*common.ImportFile{ - { - Name: "test-type.py", - Content: "test-type.py validTemplateTestCaseData content", - }, - { - Name: "test.py", - Content: "test.py validTemplateTestCaseData content", - }, - { - Name: "test2.py", - Content: "test2.py validTemplateTestCaseData content", - }, -} +var ( + TestRepoBucket = "kubernetes-charts-testing" + TestRepoURL = "gs://" + TestRepoBucket + TestChartName = "frobnitz" + TestChartVersion = "0.0.1" + TestArchiveName = TestChartName + "-" + TestChartVersion + ".tgz" + TestResourceType = TestRepoURL + "/" + TestArchiveName + TestRepoType = string(repo.GCSRepoType) + TestRepoFormat = string(repo.GCSRepoFormat) + TestRepoCredentialName = "default" + TestRepoName = TestRepoBucket +) -var validConfigTestCaseData = []byte(` +var validResponseTestCaseData = []byte(` resources: - name: test-service properties: @@ -93,6 +77,26 @@ resources: var validLayoutTestCaseData = []byte(` resources: +- name: test_invocation + resources: + - name: test-service + type: Service + - name: test-rc + type: ReplicationController + - name: test3-service + type: Service + - name: test3-rc + type: ReplicationController + - name: test4-service + type: Service + - name: test4-rc + type: ReplicationController + type: gs://kubernetes-charts-testing/frobnitz-0.0.1.tgz +`) + +/* +[]byte(` +resources: - name: test properties: test-property: test-value @@ -126,18 +130,12 @@ resources: type: test2.jinja `) -var validResponseTestCaseData = ExpansionResponse{ - Config: string(validConfigTestCaseData), - Layout: string(validLayoutTestCaseData), -} - var roundTripContent = ` -config: - resources: - - name: test - type: test.py - properties: - test: test +resources: +- name: test + type: test.py + properties: + test: test ` var roundTripExpanded = ` @@ -207,35 +205,58 @@ layout: test: test ` -var roundTripTemplate = common.Template{ - Name: "TestTemplate", - Content: roundTripContent, - Imports: nil, +var roundTripResponse = &ExpandedConfiguration{ + Config: roundTripExpanded, +} + +var roundTripResponse2 = &ExpandedConfiguration{ + Config: roundTripExpanded2, +} + +var roundTripResponses = []*ExpandedConfiguration{ + roundTripResponse, + roundTripResponse2, +} +*/ + +type mockRepoProvider struct { +} + +func (m *mockRepoProvider) GetChartByReference(reference string) (*chart.Chart, repo.IChartRepo, error) { + return &chart.Chart{}, nil, nil +} + +func (m *mockRepoProvider) GetRepoByChartURL(URL string) (repo.IChartRepo, error) { + return nil, nil +} + +func (m *mockRepoProvider) GetRepoByURL(URL string) (repo.IChartRepo, error) { + return nil, nil } type ExpanderTestCase struct { Description string Error string Handler func(w http.ResponseWriter, r *http.Request) - ValidResponse *ExpandedTemplate + ValidResponse *ExpandedConfiguration } func TestExpandTemplate(t *testing.T) { - roundTripResponse := &ExpandedTemplate{} - if err := yaml.Unmarshal([]byte(finalExpanded), roundTripResponse); err != nil { - panic(err) - } + // roundTripResponse := &ExpandedConfiguration{} + // if err := yaml.Unmarshal([]byte(finalExpanded), roundTripResponse); err != nil { + // panic(err) + // } tests := []ExpanderTestCase{ { - "expect success for ExpandTemplate", + "expect success for ExpandConfiguration", "", expanderSuccessHandler, - getValidResponse(t, "expect success for ExpandTemplate"), + getValidExpandedConfiguration(), }, { - "expect error for ExpandTemplate", - "cannot expand template", + "expect error for ExpandConfiguration", + "simulated failure", expanderErrorHandler, nil, }, @@ -245,12 +266,23 @@ func TestExpandTemplate(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(etc.Handler)) defer ts.Close() - expander := NewExpander(ts.URL) - actualResponse, err := expander.ExpandTemplate(&validTemplateTestCaseData) + expander := NewExpander(ts.URL, getTestRepoProvider(t)) + resource := &common.Resource{ + Name: "test_invocation", + Type: TestResourceType, + } + + conf := &common.Configuration{ + Resources: []*common.Resource{ + resource, + }, + } + + actualResponse, err := expander.ExpandConfiguration(conf) if err != nil { message := err.Error() if etc.Error == "" { - t.Errorf("Error in test case %s when there should not be.", etc.Description) + t.Errorf("unexpected error in test case %s: %s", etc.Description, err) } if !strings.Contains(message, etc.Error) { t.Errorf("error in test case:%s:%s\n", etc.Description, message) @@ -270,42 +302,45 @@ func TestExpandTemplate(t *testing.T) { } } -func getValidResponse(t *testing.T, description string) *ExpandedTemplate { - response, err := validResponseTestCaseData.Unmarshal() - if err != nil { - t.Errorf("cannot unmarshal valid response for test case '%s': %s\n", description, err) +func getValidServiceResponse() *common.Configuration { + conf := &common.Configuration{} + if err := yaml.Unmarshal(validResponseTestCaseData, conf); err != nil { + panic(fmt.Errorf("cannot unmarshal valid response: %s\n", err)) } - return response + return conf } -func expanderErrorHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - http.Error(w, "something failed", http.StatusInternalServerError) -} +func getValidExpandedConfiguration() *ExpandedConfiguration { + conf := getValidServiceResponse() + layout := &common.Layout{} + if err := yaml.Unmarshal(validLayoutTestCaseData, layout); err != nil { + panic(fmt.Errorf("cannot unmarshal valid response: %s\n", err)) + } -var roundTripResponse = ExpansionResponse{ - Config: roundTripExpanded, - Layout: roundTripLayout, -} + return &ExpandedConfiguration{Config: conf, Layout: layout} -var roundTripResponse2 = ExpansionResponse{ - Config: roundTripExpanded2, - Layout: roundTripLayout2, } -var roundTripResponses = []ExpansionResponse{ - roundTripResponse, - roundTripResponse2, +func expanderErrorHandler(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + http.Error(w, "simulated failure", http.StatusInternalServerError) } +/* func roundTripHandler(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() handler := "expandybird: expand" util.LogHandlerEntry(handler, r) + if len(roundTripResponses) < 1 { + http.Error(w, "Too many calls to round trip handler", http.StatusInternalServerError) + return + } + util.LogHandlerExitWithJSON(handler, w, roundTripResponses[0], http.StatusOK) roundTripResponses = roundTripResponses[1:] } +*/ func expanderSuccessHandler(w http.ResponseWriter, r *http.Request) { handler := "expandybird: expand" @@ -318,19 +353,37 @@ func expanderSuccessHandler(w http.ResponseWriter, r *http.Request) { return } - template := &common.Template{} - if err := json.Unmarshal(body, template); err != nil { + svcReq := &expansion.ServiceRequest{} + if err := json.Unmarshal(body, svcReq); err != nil { status := fmt.Sprintf("cannot unmarshal request body:%s\n%s\n", err, body) http.Error(w, status, http.StatusInternalServerError) return } - if !reflect.DeepEqual(validTemplateTestCaseData, *template) { - status := fmt.Sprintf("error in http handler:\nwant:%s\nhave:%s\n", - util.ToJSONOrError(validTemplateTestCaseData), util.ToJSONOrError(template)) - http.Error(w, status, http.StatusInternalServerError) - return + /* + if !reflect.DeepEqual(validRequestTestCaseData, *svcReq) { + status := fmt.Sprintf("error in http handler:\nwant:%s\nhave:%s\n", + util.ToJSONOrError(validRequestTestCaseData), util.ToJSONOrError(template)) + http.Error(w, status, http.StatusInternalServerError) + return + } + */ + + svcResp := getValidServiceResponse() + util.LogHandlerExitWithJSON(handler, w, svcResp, http.StatusOK) +} + +func getTestRepoProvider(t *testing.T) repo.IRepoProvider { + rs := repo.NewInmemRepoService() + rp := repo.NewRepoProvider(rs, nil, nil) + tr, err := repo.NewRepo(TestRepoURL, TestRepoCredentialName, TestRepoName, TestRepoFormat, TestRepoType) + if err != nil { + t.Fatalf("cannot create test repository: %s", err) + } + + if err := rs.CreateRepo(tr); err != nil { + t.Fatalf("cannot initialize repository service: %s", err) } - util.LogHandlerExitWithJSON(handler, w, validResponseTestCaseData, http.StatusOK) + return rp } diff --git a/cmd/manager/manager/manager.go b/cmd/manager/manager/manager.go index 87bae259e..2b148ac60 100644 --- a/cmd/manager/manager/manager.go +++ b/cmd/manager/manager/manager.go @@ -33,14 +33,14 @@ type Manager interface { // Deployments ListDeployments() ([]common.Deployment, error) GetDeployment(name string) (*common.Deployment, error) - CreateDeployment(t *common.Template) (*common.Deployment, error) + CreateDeployment(depReq *common.DeploymentRequest) (*common.Deployment, error) DeleteDeployment(name string, forget bool) (*common.Deployment, error) - PutDeployment(name string, t *common.Template) (*common.Deployment, error) + PutDeployment(name string, depReq *common.DeploymentRequest) (*common.Deployment, error) // Manifests ListManifests(deploymentName string) (map[string]*common.Manifest, error) GetManifest(deploymentName string, manifest string) (*common.Manifest, error) - Expand(t *common.Template) (*common.Manifest, error) + Expand(t *common.DeploymentRequest) (*common.Manifest, error) // Charts ListCharts() ([]string, error) @@ -125,27 +125,27 @@ func (m *manager) GetManifest(deploymentName string, manifestName string) (*comm return d, nil } -// CreateDeployment expands the supplied template, creates the resulting -// configuration in the cluster, creates a new deployment that tracks it, -// and stores the deployment in the repository. Returns the deployment. -func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, error) { - log.Printf("Creating deployment: %s", t.Name) - _, err := m.repository.CreateDeployment(t.Name) +// CreateDeployment expands the supplied configuration, creates the resulting +// resources in the cluster, creates a new deployment that tracks it, stores the +// deployment in the repository and returns the deployment. +func (m *manager) CreateDeployment(depReq *common.DeploymentRequest) (*common.Deployment, error) { + log.Printf("Creating deployment: %s", depReq.Name) + _, err := m.repository.CreateDeployment(depReq.Name) if err != nil { log.Printf("CreateDeployment failed %v", err) return nil, err } - manifest, err := m.createManifest(t) + manifest, err := m.Expand(depReq) if err != nil { log.Printf("Manifest creation failed: %v", err) - m.repository.SetDeploymentState(t.Name, failState(err)) + m.repository.SetDeploymentState(depReq.Name, failState(err)) return nil, err } if err := m.repository.AddManifest(manifest); err != nil { log.Printf("AddManifest failed %v", err) - m.repository.SetDeploymentState(t.Name, failState(err)) + m.repository.SetDeploymentState(depReq.Name, failState(err)) return nil, err } @@ -153,7 +153,7 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro if err != nil { // Deployment failed, mark as failed log.Printf("CreateConfiguration failed: %v", err) - m.repository.SetDeploymentState(t.Name, failState(err)) + m.repository.SetDeploymentState(depReq.Name, failState(err)) // If we failed before being able to create some of the resources, then // return the failure as such. Otherwise, we're going to add the manifest @@ -166,40 +166,24 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro errs := getResourceErrors(actualConfig) if len(errs) > 0 { e := fmt.Errorf("Found resource errors during deployment: %v", errs) - m.repository.SetDeploymentState(t.Name, failState(e)) + m.repository.SetDeploymentState(depReq.Name, failState(e)) return nil, e } - m.repository.SetDeploymentState(t.Name, &common.DeploymentState{Status: common.DeployedStatus}) + m.repository.SetDeploymentState(depReq.Name, &common.DeploymentState{Status: common.DeployedStatus}) } // Update the manifest with the actual state of the reified resources manifest.ExpandedConfig = actualConfig if err := m.repository.SetManifest(manifest); err != nil { log.Printf("SetManifest failed %v", err) - m.repository.SetDeploymentState(t.Name, failState(err)) + m.repository.SetDeploymentState(depReq.Name, failState(err)) return nil, err } // Finally update the type instances for this deployment. - m.setChartInstances(t.Name, manifest.Name, manifest.Layout) - return m.repository.GetValidDeployment(t.Name) -} - -func (m *manager) createManifest(t *common.Template) (*common.Manifest, error) { - et, err := m.expander.ExpandTemplate(t) - if err != nil { - log.Printf("Expansion failed %v", err) - return nil, err - } - - return &common.Manifest{ - Name: generateManifestName(), - Deployment: t.Name, - InputConfig: t, - ExpandedConfig: et.Config, - Layout: et.Layout, - }, nil + m.setChartInstances(depReq.Name, manifest.Name, manifest.Layout) + return m.repository.GetValidDeployment(depReq.Name) } func (m *manager) setChartInstances(deploymentName string, manifestName string, layout *common.Layout) { @@ -284,13 +268,13 @@ func (m *manager) DeleteDeployment(name string, forget bool) (*common.Deployment // PutDeployment replaces the configuration of the deployment with // the supplied identifier in the cluster, and returns the deployment. -func (m *manager) PutDeployment(name string, t *common.Template) (*common.Deployment, error) { +func (m *manager) PutDeployment(name string, depReq *common.DeploymentRequest) (*common.Deployment, error) { _, err := m.repository.GetValidDeployment(name) if err != nil { return nil, err } - manifest, err := m.createManifest(t) + manifest, err := m.Expand(depReq) if err != nil { log.Printf("Manifest creation failed: %v", err) m.repository.SetDeploymentState(name, failState(err)) @@ -311,20 +295,23 @@ func (m *manager) PutDeployment(name string, t *common.Template) (*common.Deploy } // Finally update the type instances for this deployment. - m.setChartInstances(t.Name, manifest.Name, manifest.Layout) - return m.repository.GetValidDeployment(t.Name) + m.setChartInstances(depReq.Name, manifest.Name, manifest.Layout) + return m.repository.GetValidDeployment(depReq.Name) } -func (m *manager) Expand(t *common.Template) (*common.Manifest, error) { - et, err := m.expander.ExpandTemplate(t) +func (m *manager) Expand(depReq *common.DeploymentRequest) (*common.Manifest, error) { + expConf, err := m.expander.ExpandConfiguration(&depReq.Configuration) if err != nil { log.Printf("Expansion failed %v", err) return nil, err } return &common.Manifest{ - ExpandedConfig: et.Config, - Layout: et.Layout, + Name: generateManifestName(), + Deployment: depReq.Name, + InputConfig: &depReq.Configuration, + ExpandedConfig: expConf.Config, + Layout: expConf.Layout, }, nil } @@ -386,7 +373,7 @@ func (m *manager) RemoveRepo(repoName string) error { return m.service.DeleteRepo(repoURL) } -// GetRepo returns the repository with the given URL +// GetRepo returns the repository with the given name func (m *manager) GetRepo(repoName string) (repo.IRepo, error) { repoURL, err := m.service.GetRepoURLByName(repoName) if err != nil { diff --git a/cmd/manager/manager/manager_test.go b/cmd/manager/manager/manager_test.go index 64bcc1901..150431630 100644 --- a/cmd/manager/manager/manager_test.go +++ b/cmd/manager/manager/manager_test.go @@ -26,8 +26,6 @@ import ( "testing" ) -var template = common.Template{Name: "test", Content: "test"} - var layout = common.Layout{ Resources: []*common.LayoutResource{{Resource: common.Resource{Name: "test", Type: "test"}}}, } @@ -47,7 +45,9 @@ var resourcesWithFailureState = common.Configuration{ }, }}, } -var expandedConfig = ExpandedTemplate{ +var deploymentRequest = common.DeploymentRequest{Name: "test", Configuration: configuration} + +var expandedConfig = ExpandedConfiguration{ Config: &configuration, Layout: &layout, } @@ -70,8 +70,8 @@ var errTest = errors.New("test error") type expanderStub struct{} -func (expander *expanderStub) ExpandTemplate(t *common.Template) (*ExpandedTemplate, error) { - if reflect.DeepEqual(*t, template) { +func (expander *expanderStub) ExpandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) { + if reflect.DeepEqual(conf, &configuration) { return &expandedConfig, nil } @@ -333,25 +333,25 @@ func TestGetManifest(t *testing.T) { func TestCreateDeployment(t *testing.T) { testRepository.reset() testDeployer.reset() - d, err := testManager.CreateDeployment(&template) + d, err := testManager.CreateDeployment(&deploymentRequest) if !reflect.DeepEqual(d, &deployment) || err != nil { t.Fatalf("Expected a different set of response values from invoking CreateDeployment."+ "Received: %v, %s. Expected: %#v, %s.", d, err, &deployment, "nil") } - if testRepository.Created[0] != template.Name { + if testRepository.Created[0] != deploymentRequest.Name { t.Fatalf("Repository CreateDeployment was called with %s but expected %s.", - testRepository.Created[0], template.Name) + testRepository.Created[0], deploymentRequest.Name) } - if !strings.HasPrefix(testRepository.ManifestAdd[template.Name].Name, "manifest-") { + if !strings.HasPrefix(testRepository.ManifestAdd[deploymentRequest.Name].Name, "manifest-") { t.Fatalf("Repository AddManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) + "to begin with manifest-.", testRepository.ManifestAdd[deploymentRequest.Name].Name) } - if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") { + if !strings.HasPrefix(testRepository.ManifestSet[deploymentRequest.Name].Name, "manifest-") { t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestSet[template.Name].Name) + "to begin with manifest-.", testRepository.ManifestSet[deploymentRequest.Name].Name) } if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil { @@ -376,11 +376,11 @@ func TestCreateDeploymentCreationFailure(t *testing.T) { testRepository.reset() testDeployer.reset() testDeployer.FailCreate = true - d, err := testManager.CreateDeployment(&template) + d, err := testManager.CreateDeployment(&deploymentRequest) - if testRepository.Created[0] != template.Name { + if testRepository.Created[0] != deploymentRequest.Name { t.Fatalf("Repository CreateDeployment was called with %s but expected %s.", - testRepository.Created[0], template.Name) + testRepository.Created[0], deploymentRequest.Name) } if len(testRepository.Deleted) != 0 { @@ -406,11 +406,11 @@ func TestCreateDeploymentCreationResourceFailure(t *testing.T) { testRepository.reset() testDeployer.reset() testDeployer.FailCreateResource = true - d, err := testManager.CreateDeployment(&template) + d, err := testManager.CreateDeployment(&deploymentRequest) - if testRepository.Created[0] != template.Name { + if testRepository.Created[0] != deploymentRequest.Name { t.Fatalf("Repository CreateDeployment was called with %s but expected %s.", - testRepository.Created[0], template.Name) + testRepository.Created[0], deploymentRequest.Name) } if len(testRepository.Deleted) != 0 { @@ -422,14 +422,18 @@ func TestCreateDeploymentCreationResourceFailure(t *testing.T) { t.Fatal("CreateDeployment failure did not mark deployment as failed") } - if !strings.HasPrefix(testRepository.ManifestAdd[template.Name].Name, "manifest-") { - t.Fatalf("Repository AddManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) + if manifest, ok := testRepository.ManifestAdd[deploymentRequest.Name]; ok { + if !strings.HasPrefix(manifest.Name, "manifest-") { + t.Fatalf("Repository AddManifest was called with %s but expected manifest name"+ + "to begin with manifest-.", manifest.Name) + } } - if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") { - t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestSet[template.Name].Name) + if manifest, ok := testRepository.ManifestSet[deploymentRequest.Name]; ok { + if !strings.HasPrefix(manifest.Name, "manifest-") { + t.Fatalf("Repository AddManifest was called with %s but expected manifest name"+ + "to begin with manifest-.", manifest.Name) + } } if err != nil || !reflect.DeepEqual(d, &deployment) { @@ -445,25 +449,25 @@ func TestCreateDeploymentCreationResourceFailure(t *testing.T) { func TestDeleteDeploymentForget(t *testing.T) { testRepository.reset() testDeployer.reset() - d, err := testManager.CreateDeployment(&template) + d, err := testManager.CreateDeployment(&deploymentRequest) if !reflect.DeepEqual(d, &deployment) || err != nil { t.Fatalf("Expected a different set of response values from invoking CreateDeployment."+ "Received: %v, %s. Expected: %#v, %s.", d, err, &deployment, "nil") } - if testRepository.Created[0] != template.Name { + if testRepository.Created[0] != deploymentRequest.Name { t.Fatalf("Repository CreateDeployment was called with %s but expected %s.", - testRepository.Created[0], template.Name) + testRepository.Created[0], deploymentRequest.Name) } - if !strings.HasPrefix(testRepository.ManifestAdd[template.Name].Name, "manifest-") { + if !strings.HasPrefix(testRepository.ManifestAdd[deploymentRequest.Name].Name, "manifest-") { t.Fatalf("Repository AddManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) + "to begin with manifest-.", testRepository.ManifestAdd[deploymentRequest.Name].Name) } - if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") { + if !strings.HasPrefix(testRepository.ManifestSet[deploymentRequest.Name].Name, "manifest-") { t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+ - "to begin with manifest-.", testRepository.ManifestSet[template.Name].Name) + "to begin with manifest-.", testRepository.ManifestSet[deploymentRequest.Name].Name) } if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil { @@ -492,21 +496,9 @@ func TestDeleteDeploymentForget(t *testing.T) { } func TestExpand(t *testing.T) { - m, err := testManager.Expand(&template) + m, err := testManager.Expand(&deploymentRequest) if err != nil { - t.Fatal("Failed to expand template into manifest.") - } - - if m.Name != "" { - t.Fatalf("Name was not empty: %v", *m) - } - - if m.Deployment != "" { - t.Fatalf("Deployment was not empty: %v", *m) - } - - if m.InputConfig != nil { - t.Fatalf("Input config not nil: %v", *m) + t.Fatal("Failed to expand deployment request into manifest.") } if !reflect.DeepEqual(*m.ExpandedConfig, configuration) { diff --git a/cmd/manager/testutil.go b/cmd/manager/testutil.go index 754759d21..20d43ddd7 100644 --- a/cmd/manager/testutil.go +++ b/cmd/manager/testutil.go @@ -83,7 +83,7 @@ func (m *mockManager) GetDeployment(name string) (*common.Deployment, error) { return nil, errors.New("mock error: No such deployment") } -func (m *mockManager) CreateDeployment(t *common.Template) (*common.Deployment, error) { +func (m *mockManager) CreateDeployment(depReq *common.DeploymentRequest) (*common.Deployment, error) { return &common.Deployment{}, nil } @@ -91,7 +91,7 @@ func (m *mockManager) DeleteDeployment(name string, forget bool) (*common.Deploy return &common.Deployment{}, nil } -func (m *mockManager) PutDeployment(name string, t *common.Template) (*common.Deployment, error) { +func (m *mockManager) PutDeployment(name string, depReq *common.DeploymentRequest) (*common.Deployment, error) { return &common.Deployment{}, nil } @@ -103,7 +103,7 @@ func (m *mockManager) GetManifest(deploymentName string, manifest string) (*comm return &common.Manifest{}, nil } -func (m *mockManager) Expand(t *common.Template) (*common.Manifest, error) { +func (m *mockManager) Expand(depReq *common.DeploymentRequest) (*common.Manifest, error) { return &common.Manifest{}, nil } diff --git a/pkg/client/deployments.go b/pkg/client/deployments.go index 30bf0ff45..dc355e4d8 100644 --- a/pkg/client/deployments.go +++ b/pkg/client/deployments.go @@ -110,8 +110,9 @@ func (c *Client) DescribeDeployment(name string) (*common.Deployment, error) { // PostDeployment posts a deployment object to the manager service. func (c *Client) PostDeployment(res *common.Resource) error { // This is a stop-gap until we get this API cleaned up. - t := common.CreateDeploymentRequest{ - ChartInvocation: res, + t := common.DeploymentRequest{ + Configuration: common.Configuration{Resources: []*common.Resource{res}}, + Name: res.Name, } data, err := json.Marshal(t) diff --git a/pkg/common/types.go b/pkg/common/types.go index b81b405a7..6d6a124c9 100644 --- a/pkg/common/types.go +++ b/pkg/common/types.go @@ -70,14 +70,15 @@ func (s DeploymentStatus) String() string { type Manifest struct { Deployment string `json:"deployment,omitempty"` Name string `json:"name,omitempty"` - InputConfig *Template `json:"inputConfig,omitempty"` + InputConfig *Configuration `json:"inputConfig,omitempty"` ExpandedConfig *Configuration `json:"expandedConfig,omitempty"` Layout *Layout `json:"layout,omitempty"` } -// CreateDeploymentRequest defines the manager API to create deployments. -type CreateDeploymentRequest struct { - ChartInvocation *Resource `json:"chart_invocation"` +// DeploymentRequest defines the manager API to create deployments. +type DeploymentRequest struct { + Configuration + Name string `json:"name"` } // ChartInstance defines the metadata for an instantiation of a chart.