Example #1
0
// ExpandChart passes the given configuration to the expander and returns the
// expanded configuration as a string on success.
func (e *expander) ExpandChart(request *expansion.ServiceRequest) (*expansion.ServiceResponse, error) {

	if err := expansion.ValidateRequest(request); err != nil {
		return nil, err
	}

	request, err := expansion.ValidateProperties(request)
	if err != nil {
		return nil, err
	}

	chartInv := request.ChartInvocation
	chartFile := request.Chart.Chartfile
	chartMembers := request.Chart.Members

	if e.ExpansionBinary == "" {
		message := fmt.Sprintf("expansion binary cannot be empty")
		return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
	}

	entrypointIndex := -1
	for i, f := range chartMembers {
		if f.Path == chartFile.Expander.Entrypoint {
			entrypointIndex = i
		}
	}
	if entrypointIndex == -1 {
		message := fmt.Sprintf("The entrypoint in the chart.yaml cannot be found: %s", chartFile.Expander.Entrypoint)
		return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
	}

	// Those are automatically increasing buffers, so writing arbitrary large
	// data here won't block the child process.
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	// Now we convert the new chart representation into the form that classic ExpandyBird takes.

	chartInvJSON, err := json.Marshal(chartInv)
	if err != nil {
		return nil, fmt.Errorf("error marshalling chart invocation %s: %s", chartInv.Name, err)
	}
	content := "{ \"resources\": [" + string(chartInvJSON) + "] }"

	cmd := &exec.Cmd{
		Path: e.ExpansionBinary,
		// Note, that binary name still has to be passed argv[0].
		Args:   []string{e.ExpansionBinary, content},
		Stdout: &stdout,
		Stderr: &stderr,
	}

	for i, f := range chartMembers {
		name := f.Path
		path := f.Path
		if i == entrypointIndex {
			// This is how expandyBird identifies the entrypoint.
			name = chartInv.Type
		}
		cmd.Args = append(cmd.Args, name, path, string(f.Content))
	}

	if err := cmd.Start(); err != nil {
		log.Printf("error starting expansion process: %s", err)
		return nil, err
	}

	cmd.Wait()

	log.Printf("Expansion process: pid: %d SysTime: %v UserTime: %v", cmd.ProcessState.Pid(),
		cmd.ProcessState.SystemTime(), cmd.ProcessState.UserTime())
	if stderr.String() != "" {
		return nil, fmt.Errorf("%s: %s", chartInv.Name, stderr.String())
	}

	output := &expandyBirdOutput{}
	if err := yaml.Unmarshal(stdout.Bytes(), output); err != nil {
		return nil, fmt.Errorf("cannot unmarshal expansion result (%s):\n%s", err, output)
	}

	return &expansion.ServiceResponse{Resources: output.Config.Resources}, nil
}
Example #2
0
// ExpandChart resolves the given files to a sequence of JSON-marshalable values.
func (e *expander) ExpandChart(request *expansion.ServiceRequest) (*expansion.ServiceResponse, error) {

	err := expansion.ValidateRequest(request)
	if err != nil {
		return nil, err
	}

	request, err = expansion.ValidateProperties(request)
	if err != nil {
		return nil, err
	}

	chartInv := request.ChartInvocation
	chartMembers := request.Chart.Members

	resources := []interface{}{}
	for _, file := range chartMembers {
		name := file.Path
		content := file.Content
		tmpl := template.New(name).Funcs(sprig.HermeticTxtFuncMap())

		for _, otherFile := range chartMembers {
			otherName := otherFile.Path
			otherContent := otherFile.Content
			if name == otherName {
				continue
			}
			_, err := tmpl.Parse(string(otherContent))
			if err != nil {
				return nil, err
			}
		}

		// Have to put something in that resolves non-empty or Go templates get confused.
		_, err := tmpl.Parse("# Content begins now")
		if err != nil {
			return nil, err
		}

		tmpl, err = tmpl.Parse(string(content))
		if err != nil {
			return nil, err
		}

		generated := bytes.NewBuffer(nil)
		if err := tmpl.ExecuteTemplate(generated, name, chartInv.Properties); err != nil {
			return nil, err
		}

		stream, err := parseYAMLStream(generated)
		if err != nil {
			return nil, fmt.Errorf("%s\nContent:\n%s", err.Error(), generated)
		}

		for _, doc := range stream {
			resources = append(resources, doc)
		}
	}

	return &expansion.ServiceResponse{Resources: resources}, nil
}