|
|
@ -18,110 +18,80 @@ package expander
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"bytes"
|
|
|
|
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ghodss/yaml"
|
|
|
|
"log"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"os/exec"
|
|
|
|
"os/exec"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/ghodss/yaml"
|
|
|
|
|
|
|
|
"github.com/kubernetes/helm/pkg/common"
|
|
|
|
"github.com/kubernetes/helm/pkg/common"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Expander abstracts interactions with the expander and deployer services.
|
|
|
|
|
|
|
|
type Expander interface {
|
|
|
|
|
|
|
|
ExpandTemplate(template *common.Template) (string, error)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type expander struct {
|
|
|
|
type expander struct {
|
|
|
|
ExpansionBinary string
|
|
|
|
ExpansionBinary string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewExpander returns a new initialized Expander.
|
|
|
|
// NewExpander returns an ExpandyBird expander.
|
|
|
|
func NewExpander(binary string) Expander {
|
|
|
|
func NewExpander(binary string) common.Expander {
|
|
|
|
return &expander{binary}
|
|
|
|
return &expander{binary}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ExpansionResult describes the unmarshalled output of ExpandTemplate.
|
|
|
|
type expandyBirdConfigOutput struct {
|
|
|
|
type ExpansionResult struct {
|
|
|
|
Resources []interface{} `yaml:"resources,omitempty"`
|
|
|
|
Config map[string]interface{}
|
|
|
|
|
|
|
|
Layout map[string]interface{}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewExpansionResult creates and returns a new expansion result from
|
|
|
|
type expandyBirdOutput struct {
|
|
|
|
// the raw output of ExpandTemplate.
|
|
|
|
Config *expandyBirdConfigOutput `yaml:"config,omitempty"`
|
|
|
|
func NewExpansionResult(output string) (*ExpansionResult, error) {
|
|
|
|
Layout interface{} `yaml:"layout,omitempty"`
|
|
|
|
eResponse := &ExpansionResult{}
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal([]byte(output), eResponse); err != nil {
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("cannot unmarshal expansion result (%s):\n%s", err, output)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return eResponse, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Marshal creates and returns an ExpansionResponse from an ExpansionResult.
|
|
|
|
// ExpandChart passes the given configuration to the expander and returns the
|
|
|
|
func (eResult *ExpansionResult) Marshal() (*ExpansionResponse, error) {
|
|
|
|
// expanded configuration as a string on success.
|
|
|
|
configYaml, err := yaml.Marshal(eResult.Config)
|
|
|
|
func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.ExpansionResponse, error) {
|
|
|
|
if err != nil {
|
|
|
|
if request.ChartInvocation == nil {
|
|
|
|
return nil, fmt.Errorf("cannot marshal manifest template (%s):\n%s", err, eResult.Config)
|
|
|
|
return nil, fmt.Errorf("Request does not have invocation field")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if request.Chart == nil {
|
|
|
|
layoutYaml, err := yaml.Marshal(eResult.Layout)
|
|
|
|
return nil, fmt.Errorf("Request does not have chart field")
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("cannot marshal manifest layout (%s):\n%s", err, eResult.Layout)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &ExpansionResponse{
|
|
|
|
chartInv := request.ChartInvocation
|
|
|
|
Config: string(configYaml),
|
|
|
|
chartFile := request.Chart.Chartfile
|
|
|
|
Layout: string(layoutYaml),
|
|
|
|
chartMembers := request.Chart.Members
|
|
|
|
}, nil
|
|
|
|
schemaName := chartInv.Type + ".schema"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ExpansionResponse describes the results of marshaling an ExpansionResult.
|
|
|
|
if chartFile.Expander == nil {
|
|
|
|
type ExpansionResponse struct {
|
|
|
|
message := fmt.Sprintf("Chart JSON does not have expander field")
|
|
|
|
Config string `json:"config"`
|
|
|
|
return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
|
|
|
|
Layout string `json:"layout"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NewExpansionResponse creates and returns a new expansion response from
|
|
|
|
|
|
|
|
// the raw output of ExpandTemplate.
|
|
|
|
|
|
|
|
func NewExpansionResponse(output string) (*ExpansionResponse, error) {
|
|
|
|
|
|
|
|
eResult, err := NewExpansionResult(output)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
eResponse, err := eResult.Marshal()
|
|
|
|
if chartFile.Expander.Name != "ExpandyBird" {
|
|
|
|
if err != nil {
|
|
|
|
message := fmt.Sprintf("ExpandyBird cannot do this kind of expansion: ", chartFile.Expander.Name)
|
|
|
|
return nil, err
|
|
|
|
return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return eResponse, nil
|
|
|
|
if e.ExpansionBinary == "" {
|
|
|
|
}
|
|
|
|
message := fmt.Sprintf("expansion binary cannot be empty")
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
|
|
|
|
// Unmarshal creates and returns an ExpansionResult from an ExpansionResponse.
|
|
|
|
|
|
|
|
func (eResponse *ExpansionResponse) Unmarshal() (*ExpansionResult, error) {
|
|
|
|
|
|
|
|
var config map[string]interface{}
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal([]byte(eResponse.Config), &config); err != nil {
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("cannot unmarshal config (%s):\n%s", err, eResponse.Config)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var layout map[string]interface{}
|
|
|
|
entrypointIndex := -1
|
|
|
|
if err := yaml.Unmarshal([]byte(eResponse.Layout), &layout); err != nil {
|
|
|
|
schemaIndex := -1
|
|
|
|
return nil, fmt.Errorf("cannot unmarshal layout (%s):\n%s", err, eResponse.Layout)
|
|
|
|
for i, f := range chartMembers {
|
|
|
|
|
|
|
|
if f.Path == chartFile.Expander.Entrypoint {
|
|
|
|
|
|
|
|
entrypointIndex = i
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.Path == chartFile.Schema {
|
|
|
|
|
|
|
|
schemaIndex = i
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if entrypointIndex == -1 {
|
|
|
|
return &ExpansionResult{
|
|
|
|
message := fmt.Sprintf("The entrypoint in the chart.yaml cannot be found: %s", chartFile.Expander.Entrypoint)
|
|
|
|
Config: config,
|
|
|
|
return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
|
|
|
|
Layout: layout,
|
|
|
|
}
|
|
|
|
}, nil
|
|
|
|
if schemaIndex == -1 {
|
|
|
|
}
|
|
|
|
message := fmt.Sprintf("The schema in the chart.yaml cannot be found: %s", chartFile.Schema)
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
|
|
|
|
// ExpandTemplate passes the given configuration to the expander and returns the
|
|
|
|
|
|
|
|
// expanded configuration as a string on success.
|
|
|
|
|
|
|
|
func (e *expander) ExpandTemplate(template *common.Template) (string, error) {
|
|
|
|
|
|
|
|
if e.ExpansionBinary == "" {
|
|
|
|
|
|
|
|
message := fmt.Sprintf("expansion binary cannot be empty")
|
|
|
|
|
|
|
|
return "", fmt.Errorf("error expanding template %s: %s", template.Name, message)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Those are automatically increasing buffers, so writing arbitrary large
|
|
|
|
// Those are automatically increasing buffers, so writing arbitrary large
|
|
|
@ -129,24 +99,42 @@ func (e *expander) ExpandTemplate(template *common.Template) (string, error) {
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
var stdout bytes.Buffer
|
|
|
|
var stderr 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{
|
|
|
|
cmd := &exec.Cmd{
|
|
|
|
Path: e.ExpansionBinary,
|
|
|
|
Path: e.ExpansionBinary,
|
|
|
|
// Note, that binary name still has to be passed argv[0].
|
|
|
|
// Note, that binary name still has to be passed argv[0].
|
|
|
|
Args: []string{e.ExpansionBinary, template.Content},
|
|
|
|
Args: []string{e.ExpansionBinary, content},
|
|
|
|
// TODO(vagababov): figure out whether do we even need "PROJECT" and
|
|
|
|
|
|
|
|
// "DEPLOYMENT_NAME" variables here.
|
|
|
|
|
|
|
|
Env: append(os.Environ(), "PROJECT="+template.Name, "DEPLOYMENT_NAME="+template.Name),
|
|
|
|
|
|
|
|
Stdout: &stdout,
|
|
|
|
Stdout: &stdout,
|
|
|
|
Stderr: &stderr,
|
|
|
|
Stderr: &stderr,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, imp := range template.Imports {
|
|
|
|
if chartFile.Schema != "" {
|
|
|
|
cmd.Args = append(cmd.Args, imp.Name, imp.Path, imp.Content)
|
|
|
|
cmd.Env = []string{"VALIDATE_SCHEMA=1"}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, f := range chartMembers {
|
|
|
|
|
|
|
|
name := f.Path
|
|
|
|
|
|
|
|
path := f.Path
|
|
|
|
|
|
|
|
if i == entrypointIndex {
|
|
|
|
|
|
|
|
// This is how expandyBird identifies the entrypoint.
|
|
|
|
|
|
|
|
name = chartInv.Type
|
|
|
|
|
|
|
|
} else if i == schemaIndex {
|
|
|
|
|
|
|
|
// Doesn't matter what it was originally called, expandyBird expects to find it here.
|
|
|
|
|
|
|
|
name = schemaName
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Args = append(cmd.Args, name, path, string(f.Content))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
log.Printf("error starting expansion process: %s", err)
|
|
|
|
log.Printf("error starting expansion process: %s", err)
|
|
|
|
return "", err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cmd.Wait()
|
|
|
|
cmd.Wait()
|
|
|
@ -154,8 +142,13 @@ func (e *expander) ExpandTemplate(template *common.Template) (string, error) {
|
|
|
|
log.Printf("Expansion process: pid: %d SysTime: %v UserTime: %v", cmd.ProcessState.Pid(),
|
|
|
|
log.Printf("Expansion process: pid: %d SysTime: %v UserTime: %v", cmd.ProcessState.Pid(),
|
|
|
|
cmd.ProcessState.SystemTime(), cmd.ProcessState.UserTime())
|
|
|
|
cmd.ProcessState.SystemTime(), cmd.ProcessState.UserTime())
|
|
|
|
if stderr.String() != "" {
|
|
|
|
if stderr.String() != "" {
|
|
|
|
return "", fmt.Errorf("error expanding template %s: %s", template.Name, 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 stdout.String(), nil
|
|
|
|
return &common.ExpansionResponse{Resources: output.Config.Resources}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|