Skip to content

Commit

Permalink
tmp
Browse files Browse the repository at this point in the history
Signed-off-by: Xabier Larrakoetxea <[email protected]>
  • Loading branch information
slok committed Mar 24, 2024
1 parent 71a884d commit 5239f0d
Show file tree
Hide file tree
Showing 21 changed files with 921 additions and 505 deletions.
37 changes: 17 additions & 20 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@

FROM golang:1.20
ARG CODEGEN_VERSION="1.27.0"
ARG CONTROLLER_GEN_VERSION="0.12.0"
FROM golang:1.22 AS build-stage

WORKDIR /src
COPY ./app .
RUN CGO_ENABLED=0 go build -o /usr/local/bin/kube-code-generator

FROM golang:1.22
ARG CODEGEN_VERSION="1.30.0-beta.0"
ARG CONTROLLER_GEN_VERSION="0.14.0"


COPY --from=build-stage /usr/local/bin/kube-code-generator /usr/local/bin/kube-code-generator

RUN apt-get update && \
apt-get install -y \
git

# Code generator stuff
RUN wget http://github.com/kubernetes/code-generator/archive/kubernetes-${CODEGEN_VERSION}.tar.gz && \
mkdir -p /go/src/k8s.io/code-generator/ && \
tar zxvf kubernetes-${CODEGEN_VERSION}.tar.gz --strip 1 -C /go/src/k8s.io/code-generator/ && \
rm kubernetes-${CODEGEN_VERSION}.tar.gz && \
\
wget http://github.com/kubernetes/apimachinery/archive/kubernetes-${CODEGEN_VERSION}.tar.gz && \
mkdir -p /go/src/k8s.io/apimachinery/ && \
tar zxvf kubernetes-${CODEGEN_VERSION}.tar.gz --strip 1 -C /go/src/k8s.io/apimachinery/ && \
rm kubernetes-${CODEGEN_VERSION}.tar.gz && \
\
wget http://github.com/kubernetes/api/archive/kubernetes-${CODEGEN_VERSION}.tar.gz && \
mkdir -p /go/src/k8s.io/api/ && \
tar zxvf kubernetes-${CODEGEN_VERSION}.tar.gz --strip 1 -C /go/src/k8s.io/api/ && \
mkdir -p /tmp/k8s-code-generator/ && \
tar zxvf kubernetes-${CODEGEN_VERSION}.tar.gz --strip 1 -C /tmp/k8s-code-generator/ && \
cd /tmp/k8s-code-generator/ && go mod tidy && cd - && \
rm kubernetes-${CODEGEN_VERSION}.tar.gz && \
\
wget https://github.com/kubernetes-sigs/controller-tools/archive/v${CONTROLLER_GEN_VERSION}.tar.gz && \
tar xvf ./v${CONTROLLER_GEN_VERSION}.tar.gz && \
cd ./controller-tools-${CONTROLLER_GEN_VERSION}/ && \
go mod tidy && \
go build -o controller-gen ./cmd/controller-gen/ && \
mv ./controller-gen /usr/bin/ && \
rm -rf ../v${CONTROLLER_GEN_VERSION}.tar.gz && \
Expand All @@ -40,12 +40,9 @@ RUN addgroup --gid $gid codegen && \
adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --disabled-password --uid $uid --ingroup codegen codegen && \
chown codegen:codegen -R /go

COPY hack /hack
RUN chown codegen:codegen -R /hack && \
mv /hack/* /usr/bin

USER codegen

WORKDIR /usr/bin

CMD ["update-codegen.sh"]
ENV KUBE_CODE_GENERATOR_CODEGEN_PATH=/tmp/k8s-code-generator
ENTRYPOINT ["kube-code-generator"]
64 changes: 64 additions & 0 deletions app/cmd_gen_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"context"
"fmt"
"log/slog"
"os/exec"
"path/filepath"
"strings"
)

type GenClientRunner struct {
cmdArgs []string
codeGenPath string
apisPath string
logger *slog.Logger
}

func NewGenClientRunner(logger *slog.Logger, codeGenPath string) *GenClientRunner {
return &GenClientRunner{
codeGenPath: codeGenPath,
logger: logger,
}
}

func (g *GenClientRunner) WithWatch() *GenClientRunner {
g.cmdArgs = append(g.cmdArgs, `--with-watch`)
return g
}

func (g *GenClientRunner) WithOutputPkg(pkg string) *GenClientRunner {
g.cmdArgs = append(g.cmdArgs, `--output-pkg`, pkg)
return g
}

func (g *GenClientRunner) WithOutputDir(path string) *GenClientRunner {
g.cmdArgs = append(g.cmdArgs, `--output-dir`, path)
return g
}

func (g *GenClientRunner) WithBoilerplate(path string) *GenClientRunner {
g.cmdArgs = append(g.cmdArgs, `--boilerplate`, path)
return g
}

func (g *GenClientRunner) WithAPIsPath(path string) *GenClientRunner {
g.apisPath = path
return g
}

func (g *GenClientRunner) Run(ctx context.Context) error {
kubeCodeGenSHPath := filepath.Join(g.codeGenPath, "kube_codegen.sh")
bashCmd := fmt.Sprintf("source %s ; kube::codegen::gen_client %s %s", kubeCodeGenSHPath, strings.Join(g.cmdArgs, " "), g.apisPath)

g.logger.DebugContext(ctx, bashCmd)
cmd := exec.CommandContext(ctx, "bash", "-c", bashCmd)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error while executing bash script: %s", string(out))
}
g.logger.DebugContext(ctx, string(out))

return nil
}
49 changes: 49 additions & 0 deletions app/cmd_gen_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"context"
"fmt"
"log/slog"
"os/exec"
"path/filepath"
"strings"
)

type GenHelpersRunner struct {
cmdArgs []string
codeGenPath string
apisPath string
logger *slog.Logger
}

func NewGenHelperstRunner(logger *slog.Logger, codeGenPath string) *GenHelpersRunner {
return &GenHelpersRunner{
codeGenPath: codeGenPath,
logger: logger,
}
}

func (g *GenHelpersRunner) WithBoilerplate(path string) *GenHelpersRunner {
g.cmdArgs = append(g.cmdArgs, `--boilerplate`, path)
return g
}

func (g *GenHelpersRunner) WithAPIsPath(path string) *GenHelpersRunner {
g.apisPath = path
return g
}

func (g *GenHelpersRunner) Run(ctx context.Context) error {
kubeCodeGenSHPath := filepath.Join(g.codeGenPath, "kube_codegen.sh")
bashCmd := fmt.Sprintf("source %s ; kube::codegen::gen_helpers %s %s", kubeCodeGenSHPath, strings.Join(g.cmdArgs, " "), g.apisPath)

g.logger.DebugContext(ctx, bashCmd)
cmd := exec.CommandContext(ctx, "bash", "-c", bashCmd)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error while executing bash script: %s", string(out))
}
g.logger.DebugContext(ctx, string(out))

return nil
}
37 changes: 37 additions & 0 deletions app/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"github.com/alecthomas/kingpin/v2"
)

// CmdConfig represents the configuration of the command.
type CmdConfig struct {
Debug bool

CodeGenPath string
APIsPath string
APIsOutPath string
BoilerplatePath string
}

// NewCmdConfig returns a new command configuration.
func NewCmdConfig(args []string) (*CmdConfig, error) {
c := &CmdConfig{}
app := kingpin.New("kube-code-generator", "The easiest way to create Kubernetes CRD related Go code and manifests.")
app.DefaultEnvars()

app.Flag("debug", "Enable debug mode.").BoolVar(&c.Debug)

app.Flag("codegen-path", "The path where github.com/kubernetes/code-generator app is.").Required().StringVar(&c.CodeGenPath)

app.Flag("apis-in", "The path to the APIs root, it must be a relative path from the root where this app is execute.").Required().StringVar(&c.APIsPath)
app.Flag("go-gen-out", "The path to the Go auto generated code, it must be a relative path from the root where this app is execute.").Required().StringVar(&c.APIsOutPath)
app.Flag("boilerplate-path", "Path to boilerplate text file, this file will be used to add this information to all the autogenerated code.").StringVar(&c.BoilerplatePath)

_, err := app.Parse(args[1:])
if err != nil {
return nil, err
}

return c, nil
}
10 changes: 10 additions & 0 deletions app/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/slok/kube-code-generator

go 1.22.0

require github.com/alecthomas/kingpin/v2 v2.4.0

require (
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
)
19 changes: 19 additions & 0 deletions app/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
124 changes: 124 additions & 0 deletions app/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"context"
"fmt"
"io"
"io/fs"
"log/slog"
"os"
"path/filepath"
"regexp"
"strings"
)

var (
goModuleRegexp = regexp.MustCompile(`(?m)^module ([^\s]+)$`)
)

func run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// Load command flags and arguments.
cmdCfg, err := NewCmdConfig(args)
if err != nil {
return fmt.Errorf("could not load command configuration: %w", err)
}

loggerOps := &slog.HandlerOptions{}
if cmdCfg.Debug {
loggerOps.Level = slog.LevelDebug
}

handler := slog.NewTextHandler(stdout, loggerOps)
logger := slog.New(handler)

// Prepare.
err = os.MkdirAll(cmdCfg.APIsOutPath, os.ModePerm)
if err != nil {
return fmt.Errorf("could not create directory for go generated code: %w", err)
}

// We will require a boilerplate always.
boilerplatePath := cmdCfg.BoilerplatePath
if cmdCfg.BoilerplatePath == "" {
f, err := os.CreateTemp("", "kube-code-generator-boilerplate-")
if err != nil {
return fmt.Errorf("could not create boilerplate empty file")
}
f.Close()
defer os.Remove(f.Name())
boilerplatePath = f.Name()
}

// Start generating the code.
outputPkg, err := inferGeneratedGoCodePackage(*cmdCfg)
if err != nil {
return fmt.Errorf("could not infer the package for the go generated code: %w", err)
}
logger.InfoContext(ctx, "Go generated code package inferred", "module", outputPkg)

logger.InfoContext(ctx, "Generating Go clients code...")
err = NewGenClientRunner(logger, cmdCfg.CodeGenPath).
WithWatch().
WithBoilerplate(boilerplatePath).
WithOutputPkg(outputPkg).
WithOutputDir(cmdCfg.APIsOutPath).
WithAPIsPath(cmdCfg.APIsPath).Run(ctx)
if err != nil {
return fmt.Errorf("could not generate Go clients code: %w", err)
}

logger.InfoContext(ctx, "Generating Go helper types code...")
err = NewGenHelperstRunner(logger, cmdCfg.CodeGenPath).
WithBoilerplate(boilerplatePath).
WithAPIsPath(cmdCfg.APIsPath).Run(ctx)
if err != nil {
return fmt.Errorf("could not generate Go types code: %w", err)
}

return nil
}

func getGoModule() (string, error) {
// Check if we are in the Go project root and ge goMod.
projectRootFS := os.DirFS(".")
goMod, err := fs.ReadFile(projectRootFS, "go.mod")
if err != nil {
return "", fmt.Errorf(`error while reading "go.mod", you should execute this app from the project root: %w`, err)
}

match := goModuleRegexp.FindAllStringSubmatch(string(goMod), 1)
if len(match) < 1 || len(match[0]) < 2 {
return "", fmt.Errorf(`could not find module declaration on "go.mod"`)
}
packageName := match[0][1]

return packageName, nil
}

func inferGeneratedGoCodePackage(cmdCfg CmdConfig) (string, error) {
goModule, err := getGoModule()
if err != nil {
return "", fmt.Errorf("could not infer go project package: %w", err)
}

apisPutPath := strings.TrimSuffix(cmdCfg.APIsOutPath, "/") + "/" // Ensure slash.
apisOutPkg := filepath.Dir(apisPutPath)
if apisOutPkg != "." && apisOutPkg != "" {
return goModule + "/" + apisOutPkg, nil
}

// No extra package, out code will be in the module root.
return goModule, nil

}

func main() {
err := run(context.Background(), os.Args, os.Stdout, os.Stderr)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
os.Exit(1)
}
}
Loading

0 comments on commit 5239f0d

Please sign in to comment.