Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Feature: Automatic Creation of 'vars:' and 'varReferences:' sections #1217

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 54 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,37 +1,75 @@
BIN_NAME=kustomize
KUSTOMIZE_NAME := kustomize
PLUGINATOR_NAME := pluginator

BINDIR := bin
TOOLS_DIR := internal/tools
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin

# Binaries.
GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint
MDRIP := $(TOOLS_BIN_DIR)/mdrip
PLUGINATOR := $(TOOLS_BIN_DIR)/pluginator

COVER_FILE=coverage.out

export GO111MODULE=on

## --------------------------------------
## Tooling Binaries
## --------------------------------------

$(GOLANGCI_LINT): $(TOOLS_DIR)/go.mod # Build golangci-lint from tools folder.
cd $(TOOLS_DIR); go build -o ./bin/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint

$(MDRIP): $(TOOLS_DIR)/go.mod # Build mdrip from tools folder.
cd $(TOOLS_DIR); go build -o ./bin/mdrip github.com/monopole/mdrip

$(PLUGINATOR): $(TOOLS_DIR)/go.mod # Build pluginator from tools folder.
cd $(TOOLS_DIR); go build -o ./bin/pluginator sigs.k8s.io/kustomize/pluginator

## --------------------------------------
## Testing
## --------------------------------------

all: test build

.PHONY: test
test: generate-code test-lint test-go

.PHONY: test-go
test-go:
go test -v ./...

test-lint:
golangci-lint run ./...

generate-code:
./plugin/generateBuiltins.sh $(GOPATH)

build:
go build -o $(BIN_NAME) cmd/kustomize/main.go

install:
go install $(PWD)/cmd/kustomize
.PHONY: test-lint
test-lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run ./...

.PHONY: cover
cover:
# The plugin directory eludes coverage, and is therefore omitted
go test ./pkg/... ./k8sdeps/... ./internal/... -coverprofile=$(COVER_FILE) && \
go tool cover -html=$(COVER_FILE)


.PHONY: generate-code
generate-code: $(PLUGINATOR)
./plugin/generateBuiltins.sh $(GOPATH)

## --------------------------------------
## Binaries
## --------------------------------------

.PHONY: build
build:
cd pluginator && go build -o $(PLUGINATOR_NAME) .
cd kustomize && go build -o $(KUSTOMIZE_NAME) ./main.go

.PHONY: install
install:
cd pluginator && go install $(PWD)/pluginator
cd kustomize && go install $(PWD)/kustomize

.PHONY: clean
clean:
go clean
rm -f $(BIN_NAME)
cd kustomize && go clean && rm -f $(KUSTOMIZE_NAME)
rm -f $(COVER_FILE)

.PHONY: test build install clean generate-code test-go test-lint cover
22 changes: 10 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@ go 1.13

require (
github.com/evanphx/json-patch v4.5.0+incompatible
github.com/go-openapi/spec v0.19.2
github.com/golangci/golangci-lint v1.19.1
github.com/gorilla/mux v1.7.3 // indirect
github.com/gorilla/sessions v1.2.0 // indirect
github.com/monopole/mdrip v1.0.0
github.com/go-openapi/spec v0.19.3
github.com/golang/protobuf v1.3.2 // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/pflag v1.0.5
golang.org/x/net v0.0.0-20191011234655-491137f69257 // indirect
golang.org/x/tools v0.0.0-20191014141550-5fa5b1782b2c // indirect
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190313235455-40a48860b5ab
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.4
k8s.io/api v0.0.0-20191005115622-2e41325d9e4b
k8s.io/apimachinery v0.0.0-20191006235458-f9f2f3f8ab02
k8s.io/client-go v11.0.0+incompatible
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208
sigs.k8s.io/kustomize/pluginator v1.0.0
k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d
sigs.k8s.io/yaml v1.1.0
)
387 changes: 49 additions & 338 deletions go.sum

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions internal/tools/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module sigs.k8s.io/kustomize/internal/tools

go 1.13

require (
github.com/golangci/golangci-lint v1.19.1
github.com/monopole/mdrip v1.0.0
sigs.k8s.io/kustomize/pluginator v1.1.0 // indirect
)
425 changes: 425 additions & 0 deletions internal/tools/go.sum

Large diffs are not rendered by default.

88 changes: 69 additions & 19 deletions k8sdeps/kunstruct/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,80 @@ import (
// the field bar, with no index. The latter PathSection references the bar
// field of the first item in the foo list
type PathSection struct {
fields []string
idx int
fields []string
idx int
searchName string
searchValue string
}

func newPathSection() PathSection {
return PathSection{idx: -1}
return PathSection{idx: -1, searchName: "", searchValue: ""}
}

func appendNonEmpty(section *PathSection, field string) {
func (ps *PathSection) appendNonEmpty(field string) {
if len(field) != 0 {
section.fields = append(section.fields, field)
ps.fields = append(ps.fields, field)
}
}

func (ps *PathSection) NotIndexed() bool {
return ps.idx == -1 && ps.searchName == ""
}

func (ps *PathSection) ResolveIndex(s []interface{}) (int, bool, error) {
if ps.idx >= len(s) {
return ps.idx, false, fmt.Errorf("index %d is out of bounds", ps.idx)
}

if ps.idx != -1 {
return ps.idx, true, nil
}

for curId, subField := range s {
subMap, ok1 := subField.(map[string]interface{})
if !ok1 {
return ps.idx, false,
fmt.Errorf("%v is of the type %T, expected map[string]interface{}",
subField, subField)
}
if foundValue, ok2 := subMap[ps.searchName]; ok2 {
if stringValue, ok3 := foundValue.(string); ok3 {
if stringValue == ps.searchValue {
return curId, true, nil
}
}
}

}

return ps.idx, false, nil
}

func (ps *PathSection) parseIndex(pathElement string) {
// Assign this index to the current
// PathSection, save it to the result, then begin
// a new PathSection
tmpIdx, err := strconv.Atoi(pathElement)
if err == nil {
// We have detected an integer so an array.
ps.idx = tmpIdx
ps.searchName = ""
ps.searchValue = ""
return
}

if strings.Contains(pathElement, "=") {
// We have detected an searchKey so an array
keyPart := strings.Split(pathElement, "=")
ps.searchName = keyPart[0]
ps.searchValue = keyPart[1]
return
}

// We have detected the downwardapi syntax
ps.appendNonEmpty(pathElement)
}

func parseFields(path string) (result []PathSection, err error) {
section := newPathSection()
if !strings.Contains(path, "[") {
Expand All @@ -45,30 +105,20 @@ func parseFields(path string) (result []PathSection, err error) {
switch c {
case '.':
if !insideParentheses {
appendNonEmpty(&section, path[start:i])
section.appendNonEmpty(path[start:i])
start = i + 1
}
case '[':
if !insideParentheses {
appendNonEmpty(&section, path[start:i])
section.appendNonEmpty(path[start:i])
start = i + 1
insideParentheses = true
} else {
return nil, fmt.Errorf("nested parentheses are not allowed: %s", path)
}
case ']':
if insideParentheses {
// Assign this index to the current
// PathSection, save it to the result, then begin
// a new PathSection
tmpIdx, err := strconv.Atoi(path[start:i])
if err == nil {
// We have detected an integer so an array.
section.idx = tmpIdx
} else {
// We have detected the downwardapi syntax
appendNonEmpty(&section, path[start:i])
}
section.parseIndex(path[start:i])
result = append(result, section)
section = newPathSection()

Expand All @@ -80,7 +130,7 @@ func parseFields(path string) (result []PathSection, err error) {
}
}
if start < len(path)-1 {
appendNonEmpty(&section, path[start:])
section.appendNonEmpty(path[start:])
result = append(result, section)
}

Expand Down
14 changes: 14 additions & 0 deletions k8sdeps/kunstruct/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ func buildPath(idx int, fields ...string) PathSectionSlice {
return PathSectionSlice{PathSection{fields: fields, idx: idx}}
}

func (a PathSectionSlice) addSearchKey(name string, value string) PathSectionSlice {
a[len(a)-1].searchName = name
a[len(a)-1].searchValue = value
return a
}

func (a PathSectionSlice) addSection(idx int, fields ...string) PathSectionSlice {
return append(a, PathSection{fields: fields, idx: idx})
}
Expand Down Expand Up @@ -104,6 +110,14 @@ func TestParseField(t *testing.T) {
expectedValue: buildPath(1, "complextree").addSection(1, "field2").addSection(-1, "stringsubfield"),
errorExpected: false,
},
{
name: "validStructDownwardAPI2",
pathToField: `spec.template.spec.containers[name=main].image`,
expectedValue: buildPath(-1, "spec", "template", "spec", "containers").
addSearchKey("name", "main").
addSection(-1, "image"),
errorExpected: false,
},
}

for _, test := range tests {
Expand Down
16 changes: 10 additions & 6 deletions k8sdeps/kunstruct/kunstruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ func (fs *UnstructAdapter) selectSubtree(path string) (map[string]interface{}, [

// There are multiple sections to walk
for sectionIdx := 0; sectionIdx < lastSectionIdx; sectionIdx++ {
idx := sections[sectionIdx].idx
fields := sections[sectionIdx].fields
pathSection := sections[sectionIdx]
fields := pathSection.fields

if idx == -1 {
if pathSection.NotIndexed() {
// This section has no index
return content, fields, true, nil
}
Expand All @@ -116,10 +116,14 @@ func (fs *UnstructAdapter) selectSubtree(path string) (map[string]interface{}, [
}
s, ok := indexedField.([]interface{})
if !ok {
return content, fields, false, fmt.Errorf("%v is of the type %T, expected []interface{}", indexedField, indexedField)
return content, fields, false,
fmt.Errorf("%v is of the type %T, expected []interface{}",
indexedField, indexedField)
}
if idx >= len(s) {
return content, fields, false, fmt.Errorf("index %d is out of bounds", idx)

idx, idxFound, err := pathSection.ResolveIndex(s)
if !idxFound || err != nil {
return content, fields, idxFound, err
}

if sectionIdx == lastSectionIdx-1 {
Expand Down
Loading