cli/launchpad/org.go (80 lines of code) (raw):
package launchpad
import (
"errors"
"fmt"
"io"
"strings"
)
// orgSpecYAML defines an Organization's Spec.
type orgSpecYAML struct {
Id string `yaml:"id"` // GCP organization id.
DisplayName string `yaml:"displayName"` // Optional field to denote GCP organization name.
SubFolderSpecs []*folderSpecYAML `yaml:"folders"`
}
// orgYAML represents a GCP organization.
type orgYAML struct {
headerYAML `yaml:",inline"`
Spec orgSpecYAML `yaml:"spec"`
subFolders folders // subFolder represents validated sub directories.
}
// resId returns an internal referencable id.
func (o *orgYAML) resId() string { return fmt.Sprintf("%s.%s", Organization, o.Spec.Id) }
// validate ensures input YAML fields are correct.
//
// validate also populates subFolders.
func (o *orgYAML) validate() error {
if o.Spec.Id == "" {
return errValidationFailed
}
o.subFolders = newSubFoldersBySpecs(o.Spec.SubFolderSpecs, Organization, o.Spec.Id)
for _, f := range o.subFolders {
if err := f.validate(); err != nil {
return err
}
}
return nil
}
// addToOrg adds the organization into the assembled organization.
//
// addToOrg also recursively add organization's subFolders into the org.
func (o *orgYAML) addToOrg(ao *assembledOrg) error {
// assembledOrg.org could have already been initialized by others via reference, or explicitly
// need to copy all fields over
if err := o.mergeFields(&ao.org); err != nil {
return err
}
ao.org = *o // replace finalized org as the current org.
if err := ao.registerResource(o, nil); err != nil {
return err
}
for _, sf := range o.subFolders { // Recursively enroll sub-folders
if err := sf.addToOrg(ao); err != nil {
return err
}
}
return nil
}
// resolveReferences processes references to organization.
//
// resolveReferences takes reference from folder as a subFolder of this organization.
func (o *orgYAML) resolveReferences(refs []resourceHandler) error {
for _, ref := range refs {
switch r := ref.(type) {
case *folderYAML:
_ = o.subFolders.add(r) // silently ignore existing resource
default:
return errors.New("unable to process reference from resource")
}
}
return nil
}
// mergeFields merges all fields from input to current resource.
//
// mergeFields only operates on named resource, ignoring YAML Specs.
// However, future version can consider recursively merging all sub resources through
// additional of mergeFields requirement in resourceHandler.
func (o *orgYAML) mergeFields(oldO *orgYAML) error {
if oldO.APIVersion != "" {
o.APIVersion = oldO.APIVersion
}
if oldO.Spec.DisplayName != "" {
o.Spec.DisplayName = oldO.Spec.DisplayName
}
o.subFolders.merge(&oldO.subFolders)
// TODO (FR) recursively merge folderSpecYAML projectSpecYAML ...etc
// resolveReferences ensures output linkage is valid, hence not a priority as this is a cleanup.
// downside is {resource}SpecYAML sub-resources are misaligned.
return nil
}
// dump writes resource's string representation into provided buffer.
func (o *orgYAML) dump(ind int, buff io.Writer) error {
indent := strings.Repeat(" ", ind)
_, err := fmt.Fprintf(buff, "%s%s.%s (\"%s\")\n", indent, Organization, o.Spec.Id, o.Spec.DisplayName)
if err != nil {
return err
}
for _, sf := range o.subFolders {
err = sf.dump(ind+defaultIndentSize, buff)
if err != nil {
return err
}
}
return nil
}