Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Commit

Permalink
Add overlays to filetree (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob0h authored Aug 7, 2018
1 parent 7955ef9 commit 218776e
Show file tree
Hide file tree
Showing 9 changed files with 436 additions and 134 deletions.
9 changes: 5 additions & 4 deletions pkg/filetree/api.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package filetree

type Node struct {
Children []Node `json:"children" yaml:"children"`
Name string `json:"name" yaml:"name"`
Path string `json:"path" yaml:"path"`
HasOverlay bool `json:"hasOverlay,omitempty" yaml:"hasOverlay,omitempty"`
Children []Node `json:"children" yaml:"children"`
Name string `json:"name" yaml:"name"`
Path string `json:"path" yaml:"path"`
HasOverlay bool `json:"hasOverlay" yaml:"hasOverlay"`
IsSupported bool `json:"isSupported" yaml:"isSupported"`
}
115 changes: 104 additions & 11 deletions pkg/filetree/loader.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
package filetree

import (
"bytes"
"os"
"path"
"strings"

"github.com/ghodss/yaml"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/kubernetes-sigs/kustomize/pkg/resource"
"github.com/pkg/errors"
"github.com/replicatedhq/ship/pkg/state"
"github.com/spf13/afero"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
)

const (
CustomResourceDefinition = "CustomResourceDefinition"
OverlaysFolder = "overlays"
)

// A Loader returns a struct representation
// of a filesystem directory tree
type Loader interface {
LoadTree(root string) (*Node, error)
LoadTree(root string, kustomize *state.Kustomize) (*Node, error)
// someday this should return an overlay too
LoadFile(root string, path string) (string, error)
}
Expand All @@ -30,13 +42,14 @@ func NewLoader(
}

type aferoLoader struct {
Logger log.Logger
FS afero.Afero
Logger log.Logger
FS afero.Afero
patches map[string]string
}

func (a *aferoLoader) LoadTree(root string) (*Node, error) {

func (a *aferoLoader) LoadTree(root string, kustomize *state.Kustomize) (*Node, error) {
fs := afero.Afero{Fs: afero.NewBasePathFs(a.FS, root)}
a.patches = kustomize.Overlays["ship"].Patches

files, err := fs.ReadDir("/")
if err != nil {
Expand All @@ -48,9 +61,25 @@ func (a *aferoLoader) LoadTree(root string) (*Node, error) {
Name: "/",
Children: []Node{},
}
overlayRootNode := Node{
Path: "/",
Name: OverlaysFolder,
Children: []Node{},
}

populatedKustomization := a.loadOverlayTree(overlayRootNode)
populated, err := a.loadTree(fs, rootNode, files)
children := []Node{populated}

if len(populatedKustomization.Children) != 0 {
children = append(children, populatedKustomization)
}

return &populated, errors.Wrap(err, "load tree")
return &Node{
Path: "/",
Name: "/",
Children: children,
}, errors.Wrap(err, "load tree")
}

// todo move this to a new struct or something
Expand Down Expand Up @@ -79,9 +108,18 @@ func (a *aferoLoader) loadTree(fs afero.Afero, current Node, files []os.FileInfo
}

if !file.IsDir() {
_, hasOverlay := a.patches[filePath]

fileB, err := fs.ReadFile(filePath)
if err != nil {
return current, errors.Wrapf(err, "read file %s", file.Name())
}

return a.loadTree(fs, current.withChild(Node{
Name: file.Name(),
Path: filePath,
Name: file.Name(),
Path: filePath,
HasOverlay: hasOverlay,
IsSupported: isSupported(fileB),
}), rest)
}

Expand All @@ -108,10 +146,65 @@ func isSymlink(file os.FileInfo) bool {
return file.Mode()&os.ModeSymlink != 0
}

func isSupported(file []byte) bool {
var out unstructured.Unstructured

fileJSON, err := yaml.YAMLToJSON(file)
if err != nil {
return false
}

decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(fileJSON), 1024)
if err := decoder.Decode(&out); err != nil {
return false
}

r := resource.NewResourceFromUnstruct(out)
if r.GetKind() == CustomResourceDefinition {
return false
}

return true
}

func (n Node) withChild(child Node) Node {
return Node{
Name: n.Name,
Path: n.Path,
Children: append(n.Children, child),
Name: n.Name,
Path: n.Path,
Children: append(n.Children, child),
IsSupported: n.IsSupported,
HasOverlay: n.HasOverlay,
}
}

func (a *aferoLoader) loadOverlayTree(kustomizationNode Node) Node {
filledTree := kustomizationNode
for patchPath := range a.patches {
splitPatchPath := strings.Split(patchPath, "/")[1:]
filledTree = a.createOverlayNode(kustomizationNode, splitPatchPath)
}
return filledTree
}

func (a *aferoLoader) createOverlayNode(kustomizationNode Node, pathToOverlay []string) Node {
if len(pathToOverlay) == 0 {
return kustomizationNode
}

pathToMatch, restOfPath := pathToOverlay[0], pathToOverlay[1:]
filePath := path.Join(kustomizationNode.Path, pathToMatch)

for _, child := range kustomizationNode.Children {
if child.Path == pathToMatch {
return a.createOverlayNode(child, restOfPath)
}
}

newNode := Node{
Name: pathToMatch,
Path: filePath,
}
loadedChild := a.createOverlayNode(newNode, restOfPath)
kustomizationNode.Children = append(kustomizationNode.Children, loadedChild)
return kustomizationNode
}
19 changes: 12 additions & 7 deletions pkg/filetree/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ import (
"strings"
"testing"

"github.com/replicatedhq/ship/pkg/state"

"github.com/go-test/deep"
"github.com/replicatedhq/ship/pkg/testing/tmpfs"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

type TestCase struct {
Name string `yaml:"name"`
Mkdir []string `yaml:"mkdir"`
Touch []string `yaml:"touch"`
Read string `yaml:"read"`
Expect *Node `yaml:"expect"`
ExpectErr string `yaml:"expectErr"`
Name string `yaml:"name"`
Mkdir []string `yaml:"mkdir"`
Touch []string `yaml:"touch"`
Overlays map[string]state.Overlay `yaml:"overlays"`
Read string `yaml:"read"`
Expect *Node `yaml:"expect"`
ExpectErr string `yaml:"expectErr"`
}

func loadTestCases(t *testing.T) []TestCase {
Expand Down Expand Up @@ -57,7 +60,9 @@ func TestAferoLoader(t *testing.T) {
toRead = "/"
}

tree, err := loader.LoadTree(toRead)
tree, err := loader.LoadTree(toRead, &state.Kustomize{
Overlays: test.Overlays,
})
if test.ExpectErr == "" {
req.NoError(err)
} else {
Expand Down
Loading

0 comments on commit 218776e

Please sign in to comment.