kubectl/pkg/kinflate/tree/builder.go

187 lines
4.8 KiB
Go

/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tree
import (
"fmt"
"os"
"path/filepath"
manifest "k8s.io/kubectl/pkg/apis/manifest/v1alpha1"
cutil "k8s.io/kubectl/pkg/kinflate/configmapandsecret"
"k8s.io/kubectl/pkg/kinflate/types"
kutil "k8s.io/kubectl/pkg/kinflate/util"
"k8s.io/kubectl/pkg/kinflate/util/fs"
)
type Loader struct {
// Allows unit tests with fake filesystem.
FS fs.FileSystem
// Unexpanded manifest directory or manifest filename.
// Examples: "." or "sean-manifest.yaml"
InitialPath string
// Full expanded manifest file path.
// Examples: "/usr/local/Kube-manifest.yaml" or "/home/seans/project/sean-manifest.yaml"
FullFilePath string
}
// LoadManifestDataFromPath takes a path to a Kube-manifest.yaml or a dir that has a Kube-manifest.yaml.
// It returns a tree of ManifestData.
func (l *Loader) LoadManifestDataFromPath() (*ManifestData, error) {
m, err := l.loadManifestFileFromPath()
if err != nil {
return nil, err
}
return l.manifestToManifestData(m)
}
// loadManifestFileFromPath loads a manifest object from file.
func (l *Loader) loadManifestFileFromPath() (*manifest.Manifest, error) {
mLoader := kutil.ManifestLoader{FS: l.FS}
// Expand the initial directory or file path into the full manifest file path.
fullFilepath, err := mLoader.MakeValidManifestPath(l.InitialPath)
if err != nil {
return nil, err
}
l.FullFilePath = fullFilepath
m, err := mLoader.Read(fullFilepath)
if err != nil {
return nil, err
}
mLoader.Validate(m)
return m, err
}
// manifestToManifestData make a ManifestData given an Manifest object
func (l *Loader) manifestToManifestData(m *manifest.Manifest) (*ManifestData, error) {
mdata, err := l.loadManifestDataFromManifestFileAndResources(m)
if err != nil {
return nil, err
}
pkgs := []*ManifestData{}
for _, pkg := range m.Packages {
loader := &Loader{FS: l.FS, InitialPath: pkg}
pkgNode, err := loader.LoadManifestDataFromPath()
if err != nil {
return nil, err
}
pkgs = append(pkgs, pkgNode)
}
mdata.Packages = pkgs
return mdata, nil
}
func (l *Loader) loadManifestDataFromManifestFileAndResources(m *manifest.Manifest) (*ManifestData, error) {
mdata := &ManifestData{}
var err error
mdata.Name = m.Name
mdata.NamePrefix = NamePrefixType(m.NamePrefix)
mdata.ObjectLabels = m.ObjectLabels
mdata.ObjectAnnotations = m.ObjectAnnotations
res, err := l.loadKObjectFromPaths(m.Resources)
if err != nil {
errorMsg := fmt.Sprintf("Resource from Manifest (%s) couldn't be loaded properly.\n%v\n"+
"Please check the Resource subsection in (%s).", l.FullFilePath, err, l.FullFilePath)
return nil, fmt.Errorf(errorMsg)
}
mdata.Resources = ResourcesType(res)
pat, err := l.loadKObjectFromPaths(m.Patches)
if err != nil {
return nil, err
}
mdata.Patches = PatchesType(pat)
cms, err := cutil.MakeConfigMapsKObject(m.Configmaps)
if err != nil {
return nil, err
}
mdata.Configmaps = ConfigmapsType(cms)
sec, err := cutil.MakeGenericSecretsKObject(m.GenericSecrets)
if err != nil {
return nil, err
}
mdata.Secrets = SecretsType(sec)
TLS, err := cutil.MakeTLSSecretsKObject(m.TLSSecrets)
err = types.Merge(mdata.Secrets, TLS)
if err != nil {
return nil, err
}
return mdata, nil
}
func (l *Loader) loadKObjectFromPaths(paths []string) (types.KObject, error) {
res := types.KObject{}
for _, path := range paths {
err := l.loadKObjectFromPath(path, res)
if err != nil {
return nil, err
}
}
return res, nil
}
func (l *Loader) loadKObjectFromPath(path string, into types.KObject) error {
_, err := l.FS.Stat(path)
if err != nil {
return err
}
if into == nil {
return fmt.Errorf("cannot load object to an empty KObject")
}
var e error
filepath.Walk(path, func(filepath string, info os.FileInfo, err error) error {
if err != nil {
e = err
return err
}
// Skip all the dir
if info.IsDir() {
return nil
}
err = l.loadKObjectFromFile(filepath, into)
return nil
})
return e
}
func (l *Loader) loadKObjectFromFile(filename string, into types.KObject) error {
f, err := l.FS.Stat(filename)
if err != nil {
return err
}
if f.IsDir() {
return fmt.Errorf("%q is NOT expected to be an dir", filename)
}
content, err := l.FS.ReadFile(filename)
if err != nil {
return err
}
_, err = kutil.Decode(content, into)
if err != nil {
return err
}
return nil
}