github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/util/v6manifestparser/parser.go (about) 1 package v6manifestparser 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 9 "github.com/cloudfoundry/bosh-cli/director/template" 10 "gopkg.in/yaml.v2" 11 ) 12 13 type Parser struct { 14 Applications []Application 15 16 pathToManifest string 17 rawManifest []byte 18 validators []validatorFunc 19 hasParsed bool 20 } 21 22 func NewParser() *Parser { 23 return new(Parser) 24 } 25 26 func (parser Parser) AppNames() []string { 27 var names []string 28 for _, app := range parser.Applications { 29 names = append(names, app.Name) 30 } 31 return names 32 } 33 34 func (parser Parser) Apps() []Application { 35 return parser.Applications 36 } 37 38 func (parser Parser) ContainsManifest() bool { 39 return parser.hasParsed 40 } 41 42 func (parser Parser) ContainsMultipleApps() bool { 43 return len(parser.Applications) > 1 44 } 45 46 func (parser Parser) ContainsPrivateDockerImages() bool { 47 for _, app := range parser.Applications { 48 if app.Docker != nil && app.Docker.Username != "" { 49 return true 50 } 51 } 52 return false 53 } 54 55 func (parser Parser) FullRawManifest() []byte { 56 return parser.rawManifest 57 } 58 59 func (parser Parser) GetPathToManifest() string { 60 return parser.pathToManifest 61 } 62 63 // InterpolateAndParse reads the manifest at the provided paths, interpolates 64 // variables if a vars file is provided, and sets the current manifest to the 65 // resulting manifest. 66 // For manifests with only 1 application, appName will override the name of the 67 // single app defined. 68 // For manifests with multiple applications, appName will filter the 69 // applications and leave only a single application in the resulting parsed 70 // manifest structure. 71 func (parser *Parser) InterpolateAndParse(pathToManifest string, pathsToVarsFiles []string, vars []template.VarKV, appName string) error { 72 rawManifest, err := ioutil.ReadFile(pathToManifest) 73 if err != nil { 74 return err 75 } 76 77 tpl := template.NewTemplate(rawManifest) 78 fileVars := template.StaticVariables{} 79 80 for _, path := range pathsToVarsFiles { 81 rawVarsFile, ioerr := ioutil.ReadFile(path) 82 if ioerr != nil { 83 return ioerr 84 } 85 86 var sv template.StaticVariables 87 88 err = yaml.Unmarshal(rawVarsFile, &sv) 89 if err != nil { 90 return InvalidYAMLError{Err: err} 91 } 92 93 for k, v := range sv { 94 fileVars[k] = v 95 } 96 } 97 98 for _, kv := range vars { 99 fileVars[kv.Name] = kv.Value 100 } 101 102 rawManifest, err = tpl.Evaluate(fileVars, nil, template.EvaluateOpts{ExpectAllKeys: true}) 103 if err != nil { 104 return InterpolationError{Err: err} 105 } 106 107 parser.pathToManifest = pathToManifest 108 return parser.parse(rawManifest, appName) 109 } 110 111 func (parser Parser) RawAppManifest(appName string) ([]byte, error) { 112 var appManifest manifest 113 for _, app := range parser.Applications { 114 if app.Name == appName { 115 appManifest.Applications = []Application{app} 116 return yaml.Marshal(appManifest) 117 } 118 } 119 return nil, AppNotInManifestError{Name: appName} 120 } 121 122 func (parser *Parser) parse(manifestBytes []byte, appName string) error { 123 parser.rawManifest = manifestBytes 124 pathToManifest := parser.GetPathToManifest() 125 var raw manifest 126 127 err := yaml.Unmarshal(manifestBytes, &raw) 128 if err != nil { 129 return err 130 } 131 132 if len(raw.Applications) == 0 { 133 return errors.New("must have at least one application") 134 } 135 136 if len(raw.Applications) == 1 && appName != "" { 137 raw.Applications[0].Name = appName 138 raw.Applications[0].FullUnmarshalledApplication["name"] = appName 139 } 140 141 filteredIndex := -1 142 143 for i := range raw.Applications { 144 if raw.Applications[i].Name == "" { 145 return errors.New("Found an application with no name specified") 146 } 147 148 if raw.Applications[i].Name == appName { 149 filteredIndex = i 150 } 151 152 if raw.Applications[i].Path == "" { 153 continue 154 } 155 156 var finalPath = raw.Applications[i].Path 157 if !filepath.IsAbs(finalPath) { 158 finalPath = filepath.Join(filepath.Dir(pathToManifest), finalPath) 159 } 160 finalPath, err = filepath.EvalSymlinks(finalPath) 161 if err != nil { 162 if os.IsNotExist(err) { 163 return InvalidManifestApplicationPathError{ 164 Path: raw.Applications[i].Path, 165 } 166 } 167 return err 168 } 169 raw.Applications[i].Path = finalPath 170 } 171 172 if filteredIndex >= 0 { 173 raw.Applications = []Application{raw.Applications[filteredIndex]} 174 } else if appName != "" { 175 return AppNotInManifestError{Name: appName} 176 } 177 178 parser.Applications = raw.Applications 179 parser.rawManifest, err = yaml.Marshal(raw) 180 if err != nil { 181 return err 182 } 183 184 parser.hasParsed = true 185 return nil 186 }