From cf42e6aeb716ce596cb098783ba79b81c9e5f88f Mon Sep 17 00:00:00 2001 From: Shiming Zhang Date: Fri, 9 Jun 2023 21:07:58 +0800 Subject: [PATCH] Support windows binary --- .github/workflows/test.yaml | 58 +++++++++++++++-------- pkg/config/config.go | 2 +- pkg/config/config_test.go | 6 +-- pkg/kwokctl/runtime/binary/cluster.go | 2 +- pkg/utils/exec/cmd.go | 25 +++++++--- pkg/utils/path/path.go | 8 +++- pkg/utils/version/version.go | 2 +- pkg/utils/version/version_test.go | 6 +++ test/kwokctl/helper.sh | 6 +++ test/kwokctl/kwokctl-config-patches.yaml | 2 +- test/kwokctl/kwokctl-config-runtimes.yaml | 2 +- 11 files changed, 83 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fd4971a65e..563fe080e6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -125,24 +125,32 @@ jobs: strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest + include: + # Windows # Linux containers are not supported on Windows Runner # https://github.com/orgs/community/discussions/25491#discussioncomment-3248089 - # - windows-latest - kwokctl-runtime: - - binary - - docker - - podman - - kind - - kind-podman - exclude: + - os: windows-latest + kwokctl-runtime: binary + + # MacOS - os: macos-latest - kwokctl-runtime: podman + kwokctl-runtime: binary + - os: macos-latest + kwokctl-runtime: docker - os: macos-latest + kwokctl-runtime: kind + + # Linux + - os: ubuntu-latest + kwokctl-runtime: binary + - os: ubuntu-latest + kwokctl-runtime: docker + - os: ubuntu-latest + kwokctl-runtime: podman + - os: ubuntu-latest + kwokctl-runtime: kind + - os: ubuntu-latest kwokctl-runtime: kind-podman - include: # nerdctl is still very early and has many bugs and differences in docker behavior, # so we need to verify that it works on every release. - os: ubuntu-latest @@ -177,22 +185,33 @@ jobs: # https://www.downloadkubernetes.com - name: Download Kubernetes Source Code uses: actions/checkout@v3 - if: ${{ matrix.kwokctl-runtime == 'binary' && matrix.os == 'macos-latest' }} + if: ${{ matrix.kwokctl-runtime == 'binary' && ( matrix.os == 'macos-latest' || matrix.os == 'windows-latest' ) }} with: repository: kubernetes/kubernetes path: kubernetes ref: v1.27.3 - - name: Build Kubernetes Binary + - name: Build Kubernetes Binary for Windows + if: ${{ matrix.kwokctl-runtime == 'binary' && matrix.os == 'windows-latest' }} + shell: bash + run: | + export GOBIN="$(go env GOPATH)/bin" + cd "${GITHUB_WORKSPACE}/kubernetes" && go install ./cmd/{kube-apiserver,kube-controller-manager,kube-scheduler} + mkdir -p ~/.kwok && cat > ~/.kwok/kwok.yaml << EOF + kind: KwokctlConfiguration + apiVersion: config.kwok.x-k8s.io/v1alpha1 + options: + kubeBinaryPrefix: '${GOBIN//\\//}' + EOF + - name: Build Kubernetes Binary for MacOS if: ${{ matrix.kwokctl-runtime == 'binary' && matrix.os == 'macos-latest' }} shell: bash run: | cd "${GITHUB_WORKSPACE}/kubernetes" && make WHAT="cmd/kube-apiserver cmd/kube-controller-manager cmd/kube-scheduler" - mkdir -p ~/.kwok - cat > ~/.kwok/kwok.yaml << EOF + mkdir -p ~/.kwok && cat > ~/.kwok/kwok.yaml << EOF kind: KwokctlConfiguration apiVersion: config.kwok.x-k8s.io/v1alpha1 options: - kubeBinaryPrefix: "${GITHUB_WORKSPACE}/kubernetes/_output/bin" + kubeBinaryPrefix: '${GITHUB_WORKSPACE}/kubernetes/_output/bin' EOF - name: Install Docker for MacOS @@ -239,7 +258,7 @@ jobs: sudo systemctl daemon-reload - name: Make pki directory - if: ${{ matrix.kwokctl-runtime == 'binary' }} + if: ${{ matrix.kwokctl-runtime == 'binary' && matrix.os != 'windows-latest' }} shell: bash run: | sudo mkdir -p /var/run/kubernetes @@ -260,7 +279,6 @@ jobs: ./hack/e2e-test.sh kwokctl/kwokctl_${{ matrix.kwokctl-runtime }} - name: Test Multi Cluster - if: ${{ matrix.os == 'ubuntu-latest' }} shell: bash run: | ./hack/e2e-test.sh kwokctl/kwokctl_${{ matrix.kwokctl-runtime }}_multi_cluster diff --git a/pkg/config/config.go b/pkg/config/config.go index 746bef9581..efd90f4ca0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -443,7 +443,7 @@ func loadRawConfig(p string) ([]json.RawMessage, error) { if errors.Is(err, io.EOF) { break } - return nil, err + return nil, fmt.Errorf("failed to decode %q: %w", raw, err) } if len(raw) == 0 { // Ignore empty documents diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 370e678276..52dd4822c3 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -20,18 +20,18 @@ import ( "context" "encoding/json" "os" - "path/filepath" "reflect" "testing" "github.com/google/go-cmp/cmp" "sigs.k8s.io/kwok/pkg/apis/internalversion" + "sigs.k8s.io/kwok/pkg/utils/path" ) func TestConfig(t *testing.T) { ctx := context.Background() - config := filepath.Join(t.TempDir(), "config.yaml") + config := path.Join(t.TempDir(), "config.yaml") data := []InternalObject{ &internalversion.KwokConfiguration{}, &internalversion.KwokctlConfiguration{}, @@ -142,7 +142,7 @@ kind: KwokctlConfiguration } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p := filepath.Join(t.TempDir(), "config.yaml") + p := path.Join(t.TempDir(), "config.yaml") err := os.WriteFile(p, tt.data, 0640) if err != nil { t.Fatal(err) diff --git a/pkg/kwokctl/runtime/binary/cluster.go b/pkg/kwokctl/runtime/binary/cluster.go index ce412ebce3..e7ee4f3c8c 100644 --- a/pkg/kwokctl/runtime/binary/cluster.go +++ b/pkg/kwokctl/runtime/binary/cluster.go @@ -700,7 +700,7 @@ func (c *Cluster) Logs(ctx context.Context, name string, out io.Writer) error { f, err := os.OpenFile(logs, os.O_RDONLY, 0640) if err != nil { - return err + return fmt.Errorf("failed to open %s: %w", logs, err) } defer func() { err = f.Close() diff --git a/pkg/utils/exec/cmd.go b/pkg/utils/exec/cmd.go index 75798c76f0..4b0a30fcdb 100644 --- a/pkg/utils/exec/cmd.go +++ b/pkg/utils/exec/cmd.go @@ -33,7 +33,8 @@ import ( // ForkExec forks a new process and execs the given command. // The process will be terminated when the context is canceled. func ForkExec(ctx context.Context, dir string, name string, arg ...string) error { - pidPath := path.Join(dir, "pids", filepath.Base(name)+".pid") + baseName := getBaseName(name) + pidPath := path.Join(dir, "pids", baseName+".pid") pidData, err := os.ReadFile(pidPath) if err == nil { pid, err := strconv.Atoi(string(pidData)) @@ -44,8 +45,8 @@ func ForkExec(ctx context.Context, dir string, name string, arg ...string) error } } - logPath := path.Join(dir, "logs", filepath.Base(name)+".log") - cmdlinePath := path.Join(dir, "cmdline", filepath.Base(name)) + logPath := path.Join(dir, "logs", baseName+".log") + cmdlinePath := path.Join(dir, "cmdline", baseName) err = os.MkdirAll(filepath.Dir(pidPath), 0750) if err != nil { @@ -91,7 +92,8 @@ func ForkExec(ctx context.Context, dir string, name string, arg ...string) error // ForkExecRestart restarts the process if it is not running. func ForkExecRestart(ctx context.Context, dir string, name string) error { - cmdlinePath := path.Join(dir, "cmdline", filepath.Base(name)) + baseName := getBaseName(name) + cmdlinePath := path.Join(dir, "cmdline", baseName) data, err := os.ReadFile(cmdlinePath) if err != nil { @@ -105,7 +107,8 @@ func ForkExecRestart(ctx context.Context, dir string, name string) error { // ForkExecKill kills the process if it is running. func ForkExecKill(ctx context.Context, dir string, name string) error { - pidPath := path.Join(dir, "pids", filepath.Base(name)+".pid") + baseName := getBaseName(name) + pidPath := path.Join(dir, "pids", baseName+".pid") _, err := os.Stat(pidPath) if err != nil { // No pid file exists, which means the process has been terminated @@ -159,7 +162,8 @@ func killProcess(pid int) error { // IsRunning returns true if the process is running. func IsRunning(ctx context.Context, dir string, name string) bool { - pidPath := path.Join(dir, "pids", filepath.Base(name)+".pid") + baseName := getBaseName(name) + pidPath := path.Join(dir, "pids", baseName+".pid") pidData, err := os.ReadFile(pidPath) if err != nil { return false @@ -173,3 +177,12 @@ func IsRunning(ctx context.Context, dir string, name string) bool { } return true } + +func getBaseName(name string) string { + baseName := filepath.Base(name) + index := strings.IndexByte(baseName, '.') + if index == -1 { + return baseName + } + return baseName[:index] +} diff --git a/pkg/utils/path/path.go b/pkg/utils/path/path.go index 28b6413c06..b2cf14b669 100644 --- a/pkg/utils/path/path.go +++ b/pkg/utils/path/path.go @@ -66,7 +66,11 @@ func Expand(path string) (string, error) { } } - return filepath.Abs(path) + p, err := filepath.Abs(path) + if err != nil { + return "", err + } + return Clean(p), nil } // RelFromHome returns a path relative to the home directory. @@ -89,5 +93,5 @@ func Join(elem ...string) string { // Dir is a wrapper around filepath.Dir. func Dir(path string) string { - return filepath.Dir(path) + return Clean(filepath.Dir(path)) } diff --git a/pkg/utils/version/version.go b/pkg/utils/version/version.go index c5122b7d8a..210c608780 100644 --- a/pkg/utils/version/version.go +++ b/pkg/utils/version/version.go @@ -32,7 +32,7 @@ import ( // Version represents a semver compatible version type Version = semver.Version -var versionRegexp = regexp.MustCompile(`(kubernetes|version):? v?(\d+\.\d+\.\d+\S*)`) +var versionRegexp = regexp.MustCompile(`(kubernetes|version):? v?(\d+\.\d+\.\d+)`) // Unknown is the unknown version. var Unknown = Version{} diff --git a/pkg/utils/version/version_test.go b/pkg/utils/version/version_test.go index 52b77e01d0..7a392b023e 100644 --- a/pkg/utils/version/version_test.go +++ b/pkg/utils/version/version_test.go @@ -39,6 +39,12 @@ func TestParseFromOutput(t *testing.T) { }, want: semver.MustParse("1.26.0"), }, + { + args: args{ + s: "Kubernetes v0.0.0-master+$Format:%H$", + }, + want: semver.MustParse("0.0.0"), + }, { args: args{ s: "prometheus, version 2.35.0 (branch: HEAD)", diff --git a/test/kwokctl/helper.sh b/test/kwokctl/helper.sh index e6c0455273..29c0500e3f 100644 --- a/test/kwokctl/helper.sh +++ b/test/kwokctl/helper.sh @@ -31,6 +31,12 @@ LOCAL_PATH="${ROOT_DIR}/bin/${GOOS}/${GOARCH}" export KWOK_CONTROLLER_BINARY="${LOCAL_PATH}/kwok" export KWOKCTL_CONTROLLER_BINARY="${LOCAL_PATH}/kwokctl" + +if [[ "${GOOS}" == "windows" ]]; then + KWOK_CONTROLLER_BINARY="${KWOK_CONTROLLER_BINARY}.exe" + KWOKCTL_CONTROLLER_BINARY="${KWOKCTL_CONTROLLER_BINARY}.exe" +fi + export KWOK_CONTROLLER_IMAGE="localhost/kwok:${VERSION}" export PATH="${LOCAL_PATH}:${PATH}" diff --git a/test/kwokctl/kwokctl-config-patches.yaml b/test/kwokctl/kwokctl-config-patches.yaml index 07ffc8ba2a..a662f2f353 100644 --- a/test/kwokctl/kwokctl-config-patches.yaml +++ b/test/kwokctl/kwokctl-config-patches.yaml @@ -1,4 +1,4 @@ -apiVersion: kwok.x-k8s.io/v1alpha1 +apiVersion: config.kwok.x-k8s.io/v1alpha1 kind: KwokctlConfiguration componentsPatches: - name: kube-apiserver diff --git a/test/kwokctl/kwokctl-config-runtimes.yaml b/test/kwokctl/kwokctl-config-runtimes.yaml index df1383cb44..f771c80229 100644 --- a/test/kwokctl/kwokctl-config-runtimes.yaml +++ b/test/kwokctl/kwokctl-config-runtimes.yaml @@ -1,5 +1,5 @@ kind: KwokctlConfiguration -apiVersion: kwok.x-k8s.io/v1alpha1 +apiVersion: config.kwok.x-k8s.io/v1alpha1 options: runtimes: - none