Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add label and annotation selector code. #3279

Merged
merged 3 commits into from
Nov 29, 2020
Merged
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
8 changes: 4 additions & 4 deletions api/internal/wrappy/wnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ func (wn *WNode) MarshalJSON() ([]byte, error) {
}

// MatchesAnnotationSelector implements ifc.Kunstructured.
func (wn *WNode) MatchesAnnotationSelector(string) (bool, error) {
panic("TODO(#WNode) MatchesAnnotationSelector; implement or drop from API")
func (wn *WNode) MatchesAnnotationSelector(selector string) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not pretty confident about copying these files so we should have some tests for these functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack, will move down into kyaml as discussed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WNode implementation is now just a delegation to RNode, consistent with the plans to ditch WNode as soon as #2506 closes.

Tests added for the RNode code.

Added more of an explanation to k8sgen/doc.go

return wn.node.MatchesAnnotationSelector(selector)
}

// MatchesLabelSelector implements ifc.Kunstructured.
func (wn *WNode) MatchesLabelSelector(string) (bool, error) {
panic("TODO(#WNode) MatchesLabelSelector; implement or drop from API")
func (wn *WNode) MatchesLabelSelector(selector string) (bool, error) {
return wn.node.MatchesLabelSelector(selector)
}

// SetAnnotations implements ifc.Kunstructured.
Expand Down
16 changes: 11 additions & 5 deletions kyaml/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
MYGOBIN := $(shell go env GOPATH)/bin
export PATH := $(MYGOBIN):$(PATH)

.PHONY: generate license fix vet fmt test lint tidy
all: generate license fix vet fmt test lint tidy
.PHONY: generate license fix vet fmt test lint tidy clean
all: license fix vet fmt test lint tidy

fix:
go fix ./...
Expand All @@ -19,17 +19,23 @@ generate:

license:
(which $(MYGOBIN)/addlicense || go get github.com/google/addlicense)
$(MYGOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
$(MYGOBIN)/addlicense -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .

tidy:
go mod tidy

lint:
(which $(MYGOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
$(MYGOBIN)/golangci-lint run ./...
(which $(MYGOBIN)/golangci-lint || \
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
$(MYGOBIN)/golangci-lint \
--skip-dirs yaml/internal/k8sgen/pkg \
run ./...

test:
go test -cover ./...

vet:
go vet ./...

clean:
rm -rf yaml/internal/k8sgen/pkg
27 changes: 27 additions & 0 deletions kyaml/yaml/internal/k8sgen/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

// Package k8sgen contains code generated by copying bits of k8s.io code.
// The copier is in generator/main.go.
//
// kyaml (and indirectly kustomize) wants to reuse the k/k label selection code.
// Labels and annotations are a key part of k8s metadata.
//
// kyaml cannot depend on it via a normal Go import because kubectl will
// depend on kyaml eventually (see kubernetes-sigs/kustomize/issues/1500).
// While kubectl remains in the k/k repo, it depends on local relative symlinked
// path to a 'staging' version of k8s.io code. No code imported by kubectl can
// refer to any other version of k8s.io code.
//
// So to do what that code does, kyaml must either reimplement it or copy it.
// When/if kubectl is finally extracted from k/k to its own repo, it can
// depend on apimachinery code via normal imports, and so can kyaml.
// At that point, this copy (and copying mechanism) can be dropped.
//
// Until then, if a bug is found in a particular instance of copied k8s.io
// (unlikely, since this code is old and stable), just increment
// apiMachineryVersion below to match the fixed version, possibly change
// the files copied, re-generate, and if need be adjust call points.
//
//go:generate go run generator/main.go
package k8sgen
3 changes: 3 additions & 0 deletions kyaml/yaml/internal/k8sgen/generator/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/generator

go 1.15
213 changes: 213 additions & 0 deletions kyaml/yaml/internal/k8sgen/generator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

// A code copier launched via `go generate`.
// See k8segen/doc.go for further discussion.
package main

import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)

const (
// Splitting this so it doesn't show up in grep.
apiMachineryModule = "k8s.io" + "/" + "apimachinery"
apiMachineryVersion = "v0.17.0"
sigsK8sIo = "sigs.k8s.io"
)

var (
filesToCopy = map[string][]string{
filepath.Join("pkg", "labels"): {
"labels.go",
"selector.go",
"zz_generated.deepcopy.go",
},
filepath.Join("pkg", "selection"): {
"operator.go",
},
filepath.Join("pkg", "util", "sets"): {
"empty.go",
"string.go",
},
filepath.Join("pkg", "util", "errors"): {
"errors.go",
},
filepath.Join("pkg", "util", "validation"): {
"validation.go",
},
filepath.Join("pkg", "util", "validation", "field"): {
"errors.go",
"path.go",
},
}
)

func main() {
c := newCopier()
// c.print()
runNoOutputCommand("go", "get", c.apiMachineryModSpec())
for dir, files := range filesToCopy {
for _, n := range files {
if err := c.copyFile(dir, n); err != nil {
log.Fatal(err)
}
}
}
runNoOutputCommand(
"go", "mod", "edit", "-droprequire="+apiMachineryModule)
runNoOutputCommand("go", "mod", "tidy")
runGetOutputCommand("go", "fmt", "./...")
}

type copier struct {
goModCache string
topPackage string
srcDir string
pgmName string
}

func (c copier) apiMachineryModSpec() string {
return apiMachineryModule + "@" + apiMachineryVersion
}

func (c copier) replacementPath() string {
return filepath.Join(c.topPackage, c.subPath())
}

func (c copier) subPath() string {
return filepath.Join("internal", c.pgmName)
}

func (c copier) print() {
fmt.Printf(" apiMachineryModule: %s\n", apiMachineryModule)
fmt.Printf(" replacementPath: %s\n", c.replacementPath())
fmt.Printf(" goModCache: %s\n", c.goModCache)
fmt.Printf(" topPackage: %s\n", c.topPackage)
fmt.Printf(" subPath: %s\n", c.subPath())
fmt.Printf(" srcDir: %s\n", c.srcDir)
fmt.Printf(" apiMachineryModSpec: %s\n", c.apiMachineryModSpec())
fmt.Printf(" pgmName: %s\n", c.pgmName)
fmt.Printf(" pwd: %s\n", os.Getenv("PWD"))
}

func newCopier() copier {
tmp := copier{
pgmName: os.Getenv("GOPACKAGE"),
goModCache: runGetOutputCommand("go", "env", "GOMODCACHE"),
}
goMod := runGetOutputCommand("go", "env", "GOMOD")
topPackage := filepath.Join(goMod[:len(goMod)-len("go.mod")-1], "yaml")
k := strings.Index(topPackage, sigsK8sIo)
if k < 1 {
log.Fatalf("cannot find %s in %s", sigsK8sIo, topPackage)
}
tmp.srcDir = topPackage[:k-1]
tmp.topPackage = topPackage[k:]
return tmp
}

func (c copier) copyFile(dir, name string) error {
inFile, err := os.Open(
filepath.Join(c.goModCache, c.apiMachineryModSpec(), dir, name))
if err != nil {
return err
}
defer inFile.Close()
scanner := bufio.NewScanner(inFile)

w, err := newWriter(dir, name)
if err != nil {
return err
}
defer w.close()

w.write(
fmt.Sprintf(
// This particular phrasing is required.
"// Code generated by %s/generator from %s; DO NOT EDIT.",
c.pgmName, c.apiMachineryModSpec()))
w.write(
fmt.Sprintf(
"// Copied from %s\n",
filepath.Join(c.apiMachineryModSpec(), dir, name)))

for scanner.Scan() {
l := scanner.Text()
// Disallow recursive generation.
if strings.HasPrefix(l, "//go:generate") {
continue
}
// Don't want it to appear double generated.
if strings.HasPrefix(l, "// Code generated") {
continue
}
// Fix self-imports.
l = strings.Replace(l, apiMachineryModule, c.replacementPath(), 1)
// Replace klog with generic log (eschewing k8s.io entirely).
l = strings.Replace(l, "\"k8s.io/klog\"", "\"log\"", 1)
l = strings.Replace(l, "klog.V(10).Infof(", "log.Printf(", 1)
w.write(l)
}
if err := scanner.Err(); err != nil {
return err
}
w.write("")
return nil
}

type writer struct {
root string
f *os.File
}

func newWriter(toDir, name string) (*writer, error) {
if err := os.MkdirAll(toDir, 0755); err != nil {
log.Printf("unable to create directory: %s", toDir)
return nil, err
}
n := filepath.Join(toDir, name)
f, err := os.Create(n)
if err != nil {
return nil, fmt.Errorf("unable to create `%s`; %v", n, err)
}
return &writer{root: toDir, f: f}, nil
}

func (w *writer) close() {
w.f.Close()
}

func (w *writer) write(line string) {
if _, err := w.f.WriteString(line + "\n"); err != nil {
log.Printf("Trouble writing: %s", line)
log.Fatalf("Error: %s", err)
}
}

func runNoOutputCommand(n string, args ...string) {
o := runGetOutputCommand(n, args...)
if len(o) > 0 {
log.Fatalf("unexpected output: %q", o)
}
}

func runGetOutputCommand(n string, args ...string) string {
cmd := exec.Command(n, args...)
var outBuf bytes.Buffer
cmd.Stdout = &outBuf
var errBuf bytes.Buffer
cmd.Stderr = &errBuf
if err := cmd.Run(); err != nil {
fmt.Printf("err: %q\n", errBuf.String())
log.Fatal(err)
}
return strings.TrimSpace(outBuf.String())
}
Loading