mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-14 11:03:09 +02:00
238 lines
5.3 KiB
Go
238 lines
5.3 KiB
Go
|
package cloudfoundry
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/ghodss/yaml"
|
||
|
"github.com/pkg/errors"
|
||
|
"io/ioutil"
|
||
|
"reflect"
|
||
|
|
||
|
"github.com/SAP/jenkins-library/pkg/log"
|
||
|
)
|
||
|
|
||
|
const constPropApplications = "applications"
|
||
|
const constPropBuildpacks = "buildpacks"
|
||
|
const constPropBuildpack = "buildpack"
|
||
|
|
||
|
// Manifest ...
|
||
|
type Manifest struct {
|
||
|
self map[string]interface{}
|
||
|
modified bool
|
||
|
name string
|
||
|
}
|
||
|
|
||
|
var _readFile = ioutil.ReadFile
|
||
|
var _writeFile = ioutil.WriteFile
|
||
|
|
||
|
// ReadManifest Reads the manifest denoted by 'name'
|
||
|
func ReadManifest(name string) (Manifest, error) {
|
||
|
|
||
|
log.Entry().Infof("Reading manifest file '%s'", name)
|
||
|
|
||
|
m := Manifest{self: make(map[string]interface{}), name: name, modified: false}
|
||
|
|
||
|
content, err := _readFile(name)
|
||
|
if err != nil {
|
||
|
return m, errors.Wrapf(err, "cannot read file '%v'", m.name)
|
||
|
}
|
||
|
|
||
|
err = yaml.Unmarshal(content, &m.self)
|
||
|
if err != nil {
|
||
|
return m, errors.Wrapf(err, "Cannot parse yaml file '%s': %s", m.name, string(content))
|
||
|
}
|
||
|
|
||
|
log.Entry().Infof("Manifest file '%s' has been parsed", m.name)
|
||
|
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
// WriteManifest Writes the manifest to the file denoted
|
||
|
// by the name property (GetName()). The modified flag is
|
||
|
// resetted after the write operation.
|
||
|
func (m *Manifest) WriteManifest() error {
|
||
|
|
||
|
d, err := yaml.Marshal(&m.self)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Entry().Debugf("Writing manifest file '%s'", m.name)
|
||
|
err = _writeFile(m.name, d, 0644)
|
||
|
|
||
|
if err == nil {
|
||
|
m.modified = false
|
||
|
}
|
||
|
|
||
|
log.Entry().Debugf("Manifest file '%s' has been written", m.name)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// GetName Returns the file name of the manifest.
|
||
|
func (m Manifest) GetName() string {
|
||
|
return m.name
|
||
|
}
|
||
|
|
||
|
// GetApplications Returns all applications denoted in the manifest file.
|
||
|
// The applications are returned as a slice of maps. Each app is represented by
|
||
|
// a map.
|
||
|
func (m Manifest) GetApplications() ([]map[string]interface{}, error) {
|
||
|
apps, err := toSlice(m.self["applications"])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
result := make([]map[string]interface{}, 0)
|
||
|
|
||
|
for _, app := range apps {
|
||
|
if _app, ok := app.(map[string]interface{}); ok {
|
||
|
result = append(result, _app)
|
||
|
} else {
|
||
|
return nil, fmt.Errorf("Cannot cast applications to map. Manifest file '%s' has invalid format", m.GetName())
|
||
|
}
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
// ApplicationHasProperty Checks if the application denoted by 'index' has the property 'name'
|
||
|
func (m Manifest) ApplicationHasProperty(index int, name string) (bool, error) {
|
||
|
|
||
|
sliced, err := toSlice(m.self[constPropApplications])
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
if index >= len(sliced) {
|
||
|
return false, fmt.Errorf("Index (%d) out of bound. Number of apps: %d", index, len(sliced))
|
||
|
}
|
||
|
|
||
|
_m, err := toMap(sliced[index])
|
||
|
if err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
_, ok := _m[name]
|
||
|
|
||
|
return ok, nil
|
||
|
}
|
||
|
|
||
|
// GetApplicationProperty ...
|
||
|
func (m Manifest) GetApplicationProperty(index int, name string) (interface{}, error) {
|
||
|
|
||
|
sliced, err := toSlice(m.self[constPropApplications])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if index >= len(sliced) {
|
||
|
return nil, fmt.Errorf("Index (%d) out of bound. Number of apps: %d", index, len(sliced))
|
||
|
}
|
||
|
|
||
|
app, err := toMap(sliced[index])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
value, exists := app[name]
|
||
|
if exists {
|
||
|
return value, nil
|
||
|
}
|
||
|
|
||
|
return nil, fmt.Errorf("No such property: '%s' available in application at position %d", name, index)
|
||
|
}
|
||
|
|
||
|
// GetAppName Gets the name of the app at 'index'
|
||
|
func (m Manifest) GetAppName(index int) (string, error) {
|
||
|
|
||
|
appName, err := m.GetApplicationProperty(index, "name")
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
if name, ok := appName.(string); ok {
|
||
|
return name, nil
|
||
|
}
|
||
|
|
||
|
return "", fmt.Errorf("Cannot retrieve application name for app at index %d", index)
|
||
|
}
|
||
|
|
||
|
// Transform For each app in the manifest the first entry in the build packs list
|
||
|
// gets moved to the top level under the key 'buildpack'. The 'buildpacks' list is
|
||
|
// deleted.
|
||
|
func (m *Manifest) Transform() error {
|
||
|
|
||
|
sliced, err := toSlice(m.self[constPropApplications])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
for _, app := range sliced {
|
||
|
appAsMap, err := toMap(app)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
err = transformApp(appAsMap, m)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func transformApp(app map[string]interface{}, m *Manifest) error {
|
||
|
|
||
|
appName := "n/a"
|
||
|
|
||
|
if name, ok := app["name"].(string); ok {
|
||
|
if len(name) > 0 {
|
||
|
appName = name
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if app[constPropBuildpacks] == nil {
|
||
|
// Revisit: not sure if a build pack is mandatory.
|
||
|
// In that case we should check that app.buildpack
|
||
|
// is present.
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
buildPacks, err := toSlice(app[constPropBuildpacks])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if len(buildPacks) > 1 {
|
||
|
return fmt.Errorf("More than one Cloud Foundry Buildpack is not supported. Please check manifest file '%s', application '%s'", m.name, appName)
|
||
|
}
|
||
|
|
||
|
if len(buildPacks) == 1 {
|
||
|
app[constPropBuildpack] = buildPacks[0]
|
||
|
delete(app, constPropBuildpacks)
|
||
|
m.modified = true
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// IsModified ...
|
||
|
func (m Manifest) IsModified() bool {
|
||
|
return m.modified
|
||
|
}
|
||
|
|
||
|
func toMap(i interface{}) (map[string]interface{}, error) {
|
||
|
|
||
|
if m, ok := i.(map[string]interface{}); ok {
|
||
|
return m, nil
|
||
|
}
|
||
|
return nil, fmt.Errorf("Failed to convert %v to map. Was %v", i, reflect.TypeOf(i))
|
||
|
}
|
||
|
|
||
|
func toSlice(i interface{}) ([]interface{}, error) {
|
||
|
|
||
|
if s, ok := i.([]interface{}); ok {
|
||
|
return s, nil
|
||
|
}
|
||
|
return nil, fmt.Errorf("Failed to convert %v to slice. Was %v", i, reflect.TypeOf(i))
|
||
|
}
|