in cmd/hub/lifecycle/backup.go [30:270]
func BackupCreate(request *Request, bundles []string, jsonOutput, allowPartial bool, pipe io.WriteCloser) {
if len(request.StateFilenames) == 0 {
log.Fatal("Backup without state file(s) is not implemented; try --state")
}
verb := maybeTestVerb(request.Verb, request.DryRun)
if len(bundles) == 0 && config.Verbose && !config.Debug {
config.Verbose = false
config.AggWarnings = false
}
warnBackupFlagsNotImplemented(request)
stackManifest, componentsManifests, _, err := manifest.ParseManifest(request.ManifestFilenames)
if err != nil {
log.Fatalf("Unable to create backup: %v", err)
}
if pipe != nil {
metricTags := fmt.Sprintf("stack:%s", stackManifest.Meta.Name)
pipe.Write([]byte(metricTags))
pipe.Close()
}
osEnv, err := initOsEnv(request.OsEnvironmentMode)
if err != nil {
log.Fatalf("Unable to parse OS environment setup: %v", err)
}
stackBaseDir := util.Basedir(request.ManifestFilenames)
componentsBaseDir := request.ComponentsBaseDir
if componentsBaseDir == "" {
componentsBaseDir = stackBaseDir
}
order, err := manifest.GenerateLifecycleOrder(stackManifest)
if err != nil {
log.Fatal(err)
}
stackManifest.Lifecycle.Order = order
components := stackManifest.Components
checkComponentsManifests(components, componentsManifests)
if len(request.Components) > 0 {
manifest.CheckComponentsExist(components, request.Components...)
components = make([]manifest.ComponentRef, 0, len(request.Components))
for _, componentName := range request.Components {
componentRef := manifest.ComponentRefByName(stackManifest.Components, componentName)
components = append(components, *componentRef)
}
order = request.Components
}
checkComponentsSourcesExist(order, components, stackBaseDir, componentsBaseDir, nil)
if len(request.Components) == 0 {
checkLifecycleOrder(components, stackManifest.Lifecycle)
}
implementsBackup := make([]string, 0, len(order))
for _, componentName := range order {
component := manifest.ComponentRefByName(components, componentName)
componentManifest := manifest.ComponentManifestByRef(componentsManifests, component)
if util.Contains(componentManifest.Lifecycle.Verbs, request.Verb) {
dir := manifest.ComponentSourceDirFromRef(component, stackBaseDir, componentsBaseDir)
impl, _ := probeImplementation(dir, verb, componentManifest)
if impl {
implementsBackup = append(implementsBackup, manifest.ComponentQualifiedNameFromRef(component))
}
}
}
if len(request.Components) == 0 && len(implementsBackup) == 0 {
log.Fatalf("No component implements `%s` verb", verb)
}
if len(request.Components) > 0 && len(request.Components) != len(implementsBackup) {
for _, componentName := range request.Components {
if !util.Contains(implementsBackup, componentName) {
log.Printf("Component `%s` does not implement `%s` verb", componentName, verb)
}
}
os.Exit(1)
}
optionalRequires := parseRequiresTunning(stackManifest.Lifecycle.Requires)
requiresOfOptionalComponents := calculateRequiresOfOptionalComponents(componentsManifests, &stackManifest.Lifecycle, stackManifest.Requires)
stackRequires := maybeOmitCloudRequires(stackManifest.Requires, request.EnabledClouds)
stackProvides := checkStackRequires(stackRequires, optionalRequires, requiresOfOptionalComponents)
stateFiles, errs := storage.Check(request.StateFilenames, "state")
if len(errs) > 0 {
util.MaybeFatalf("Unable to check state files: %v", util.Errors2(errs...))
}
var bundleFiles *storage.Files
if len(bundles) > 0 {
checked, errs := storage.Check(bundles, "backup bundle")
if len(errs) > 0 {
util.MaybeFatalf("Unable to check backup bundle files: %v", util.Errors2(errs...))
}
bundleFiles = checked
}
if config.Verbose {
printBackupStartBlurb(request, bundles)
}
parsedState, err := state.ParseState(stateFiles)
if err != nil {
util.MaybeFatalf("Unable to load state %v: %v", request.StateFilenames, err)
}
if config.Verbose {
log.Printf("%s %v", cases.Title(language.Und).String(verb), implementsBackup)
}
bundle := state.BackupManifest{
Version: 1,
Kind: "backup",
Components: make(map[string]state.ComponentBackup),
}
failedComponents := make([]string, 0)
for componentIndex, componentName := range implementsBackup {
if config.Verbose {
log.Printf("%s ***%s*** (%d/%d)", verb, componentName, componentIndex+1, len(implementsBackup))
}
component := manifest.ComponentRefByName(components, componentName)
componentManifest := manifest.ComponentManifestByRef(componentsManifests, component)
// TODO Should we reload new parameters from elaborate to allow for component's source mismatch?
// Or it will encourage bad practice?
stackParameters := make(parameters.LockedParameters)
allOutputs := make(parameters.CapturedOutputs)
provides := util.CopyMap2(stackProvides)
if parsedState != nil {
state.MergeParsedState(parsedState,
componentName, component.Depends, stackManifest.Lifecycle.Order, false,
stackParameters, allOutputs, provides)
}
expandedComponentParameters, errs := parameters.ExpandParameters(componentName, componentManifest.Meta.Kind, component.Depends,
stackParameters, allOutputs,
manifest.FlattenParameters(componentManifest.Parameters, componentManifest.Meta.Name))
if len(errs) > 0 {
util.MaybeFatalf("Component `%s` parameters expansion failed:\n\t%s",
componentName, util.Errors("\n\t", errs...))
}
componentParameters := parameters.MergeParameters(make(parameters.LockedParameters), expandedComponentParameters)
if config.Debug {
log.Print("Component parameters:")
parameters.PrintLockedParameters(componentParameters)
}
prepareComponentRequires(provides, componentManifest, stackParameters, allOutputs, optionalRequires, request.EnabledClouds)
dir := manifest.ComponentSourceDirFromRef(component, stackBaseDir, componentsBaseDir)
stdout, _, err := delegate(verb, component, componentManifest, componentParameters, dir, osEnv, "", stackBaseDir)
var rawOutputs parameters.RawOutputs
if len(stdout) > 0 {
rawOutputs = parseTextOutput(stdout)
}
status := "error"
if err != nil || len(rawOutputs) == 0 {
if err == nil {
err = errors.New("no outputs emited by the component")
}
log.Printf("Component `%s` failed to %s: %v", componentName, verb, err)
failedComponents = append(failedComponents, componentName)
if !allowPartial {
break
}
} else {
status = "success"
log.Printf("Component `%s` completed %s", componentName, verb)
}
kind, exist := rawOutputs["kind"]
if !exist || kind == "" {
kind = componentName
}
timestamp := time.Now()
timestampStr, exist := rawOutputs["timestamp"]
if exist && timestampStr != "" {
timestamp2, err := time.Parse(time.RFC3339, timestampStr)
if err != nil {
util.Warn("Unable to parse timestamp `%s` emited by component `%s`: %v; using current time",
timestampStr, componentName, err)
} else {
timestamp = timestamp2
}
}
delete(rawOutputs, "kind")
delete(rawOutputs, "timestamp")
outputs := make([]parameters.CapturedOutput, 0, len(rawOutputs))
for name, value := range rawOutputs {
outputs = append(outputs, parameters.CapturedOutput{Name: name, Value: value})
}
bundle.Components[componentName] = state.ComponentBackup{
Timestamp: timestamp,
Status: status,
Kind: kind,
Outputs: outputs,
}
}
if len(failedComponents) > 0 {
log.Printf("Component(s) failed to %s: %v", verb, failedComponents)
if allowPartial && len(failedComponents) < len(implementsBackup) {
bundle.Status = "partial"
} else {
bundle.Status = "error"
}
} else {
bundle.Status = "success"
}
bundle.Timestamp = time.Now()
format := "yaml"
marshall := yaml.Marshal
if jsonOutput {
format = "json"
marshall = json.Marshal
}
bytes, err := marshall(&bundle)
if err != nil {
log.Fatalf("Unable to marshal backup bundle into %s: %v", format, err)
}
if bundleFiles != nil {
_, errs := storage.Write(bytes, bundleFiles)
if len(errs) > 0 {
log.Fatalf("Unable to write backup bundle: %s", util.Errors2(errs...))
}
} else {
os.Stdout.Write([]byte(fmt.Sprintf("--- %s\n", format)))
os.Stdout.Write(bytes)
}
if config.Verbose {
printBackupEndBlurb(request, stackManifest)
}
}