mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-09 12:07:47 +00:00
Allow override of prerelease/buildID portions of version at runtime
This commit is contained in:
parent
d94c733ee2
commit
d22b9310e6
77
staging/src/k8s.io/component-base/version/dynamic.go
Normal file
77
staging/src/k8s.io/component-base/version/dynamic.go
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
utilversion "k8s.io/apimachinery/pkg/util/version"
|
||||
)
|
||||
|
||||
var dynamicGitVersion atomic.Value
|
||||
|
||||
func init() {
|
||||
// initialize to static gitVersion
|
||||
dynamicGitVersion.Store(gitVersion)
|
||||
}
|
||||
|
||||
// SetDynamicVersion overrides the version returned as the GitVersion from Get().
|
||||
// The specified version must be non-empty, a valid semantic version, and must
|
||||
// match the major/minor/patch version of the default gitVersion.
|
||||
func SetDynamicVersion(dynamicVersion string) error {
|
||||
if err := ValidateDynamicVersion(dynamicVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
dynamicGitVersion.Store(dynamicVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateDynamicVersion ensures the given version is non-empty, a valid semantic version,
|
||||
// and matched the major/minor/patch version of the default gitVersion.
|
||||
func ValidateDynamicVersion(dynamicVersion string) error {
|
||||
return validateDynamicVersion(dynamicVersion, gitVersion)
|
||||
}
|
||||
|
||||
func validateDynamicVersion(dynamicVersion, defaultVersion string) error {
|
||||
if len(dynamicVersion) == 0 {
|
||||
return fmt.Errorf("version must not be empty")
|
||||
}
|
||||
if dynamicVersion == defaultVersion {
|
||||
// allow no-op
|
||||
return nil
|
||||
}
|
||||
vRuntime, err := utilversion.ParseSemantic(dynamicVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// must match major/minor/patch of default version
|
||||
var vDefault *utilversion.Version
|
||||
if defaultVersion == "v0.0.0-master+$Format:%H$" {
|
||||
// special-case the placeholder value which doesn't parse as a semantic version
|
||||
vDefault, err = utilversion.ParseSemantic("v0.0.0-master")
|
||||
} else {
|
||||
vDefault, err = utilversion.ParseSemantic(defaultVersion)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vRuntime.Major() != vDefault.Major() || vRuntime.Minor() != vDefault.Minor() || vRuntime.Patch() != vDefault.Patch() {
|
||||
return fmt.Errorf("version %q must match major/minor/patch of default version %q", dynamicVersion, defaultVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -20,20 +20,22 @@ package verflag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/component-base/version"
|
||||
)
|
||||
|
||||
type versionValue int
|
||||
type versionValue string
|
||||
|
||||
const (
|
||||
VersionFalse versionValue = 0
|
||||
VersionTrue versionValue = 1
|
||||
VersionRaw versionValue = 2
|
||||
VersionFalse versionValue = "false"
|
||||
VersionTrue versionValue = "true"
|
||||
VersionRaw versionValue = "raw"
|
||||
)
|
||||
|
||||
const strRawVersion string = "raw"
|
||||
@ -51,20 +53,28 @@ func (v *versionValue) Set(s string) error {
|
||||
*v = VersionRaw
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, "v") {
|
||||
err := version.SetDynamicVersion(s)
|
||||
if err == nil {
|
||||
*v = versionValue(s)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
boolVal, err := strconv.ParseBool(s)
|
||||
if boolVal {
|
||||
*v = VersionTrue
|
||||
} else {
|
||||
*v = VersionFalse
|
||||
if err == nil {
|
||||
if boolVal {
|
||||
*v = VersionTrue
|
||||
} else {
|
||||
*v = VersionFalse
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *versionValue) String() string {
|
||||
if *v == VersionRaw {
|
||||
return strRawVersion
|
||||
}
|
||||
return fmt.Sprintf("%v", bool(*v == VersionTrue))
|
||||
return string(*v)
|
||||
}
|
||||
|
||||
// The type of the flag as required by the pflag.Value interface
|
||||
@ -88,7 +98,7 @@ func Version(name string, value versionValue, usage string) *versionValue {
|
||||
const versionFlagName = "version"
|
||||
|
||||
var (
|
||||
versionFlag = Version(versionFlagName, VersionFalse, "Print version information and quit")
|
||||
versionFlag = Version(versionFlagName, VersionFalse, "--version, --version=raw prints version information and quits; --version=vX.Y.Z... sets the reported version")
|
||||
programName = "Kubernetes"
|
||||
)
|
||||
|
||||
@ -98,14 +108,20 @@ func AddFlags(fs *flag.FlagSet) {
|
||||
fs.AddFlag(flag.Lookup(versionFlagName))
|
||||
}
|
||||
|
||||
// PrintAndExitIfRequested will check if the -version flag was passed
|
||||
// variables for unit testing PrintAndExitIfRequested
|
||||
var (
|
||||
output = io.Writer(os.Stdout)
|
||||
exit = os.Exit
|
||||
)
|
||||
|
||||
// PrintAndExitIfRequested will check if --version or --version=raw was passed
|
||||
// and, if so, print the version and exit.
|
||||
func PrintAndExitIfRequested() {
|
||||
if *versionFlag == VersionRaw {
|
||||
fmt.Printf("%#v\n", version.Get())
|
||||
os.Exit(0)
|
||||
fmt.Fprintf(output, "%#v\n", version.Get())
|
||||
exit(0)
|
||||
} else if *versionFlag == VersionTrue {
|
||||
fmt.Printf("%s %s\n", programName, version.Get())
|
||||
os.Exit(0)
|
||||
fmt.Fprintf(output, "%s %s\n", programName, version.Get())
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package verflag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/component-base/version"
|
||||
)
|
||||
|
||||
func TestVersionFlag(t *testing.T) {
|
||||
initialFlagValue := string(*versionFlag)
|
||||
initialVersion := version.Get()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
flags []string
|
||||
expectError string
|
||||
expectExit bool
|
||||
expectPrintVersion string
|
||||
expectGitVersion string
|
||||
}{
|
||||
{
|
||||
name: "no flag",
|
||||
flags: []string{},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
},
|
||||
{
|
||||
name: "false",
|
||||
flags: []string{"--version=false"},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
},
|
||||
|
||||
{
|
||||
name: "valueless",
|
||||
flags: []string{"--version"},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
expectExit: true,
|
||||
expectPrintVersion: "Kubernetes " + initialVersion.GitVersion,
|
||||
},
|
||||
{
|
||||
name: "true",
|
||||
flags: []string{"--version=true"},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
expectExit: true,
|
||||
expectPrintVersion: "Kubernetes " + initialVersion.GitVersion,
|
||||
},
|
||||
{
|
||||
name: "raw",
|
||||
flags: []string{"--version=raw"},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
expectExit: true,
|
||||
expectPrintVersion: fmt.Sprintf("%#v", initialVersion),
|
||||
},
|
||||
{
|
||||
name: "truthy",
|
||||
flags: []string{"--version=T"},
|
||||
expectGitVersion: initialVersion.GitVersion,
|
||||
expectExit: true,
|
||||
expectPrintVersion: "Kubernetes " + initialVersion.GitVersion,
|
||||
},
|
||||
|
||||
{
|
||||
name: "override",
|
||||
flags: []string{"--version=v0.0.0-custom"},
|
||||
expectGitVersion: "v0.0.0-custom",
|
||||
},
|
||||
{
|
||||
name: "invalid override semver",
|
||||
flags: []string{"--version=vX"},
|
||||
expectError: `could not parse "vX"`,
|
||||
},
|
||||
{
|
||||
name: "invalid override major",
|
||||
flags: []string{"--version=v1.0.0"},
|
||||
expectError: `must match major/minor/patch`,
|
||||
},
|
||||
{
|
||||
name: "invalid override minor",
|
||||
flags: []string{"--version=v0.1.0"},
|
||||
expectError: `must match major/minor/patch`,
|
||||
},
|
||||
{
|
||||
name: "invalid override patch",
|
||||
flags: []string{"--version=v0.0.1"},
|
||||
expectError: `must match major/minor/patch`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "override and exit",
|
||||
flags: []string{"--version=v0.0.0-custom", "--version"},
|
||||
expectGitVersion: "v0.0.0-custom",
|
||||
expectExit: true,
|
||||
expectPrintVersion: "Kubernetes v0.0.0-custom",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
originalOutput := output
|
||||
originalExit := exit
|
||||
|
||||
outputBuffer := &bytes.Buffer{}
|
||||
output = outputBuffer
|
||||
exitCalled := false
|
||||
exit = func(code int) { exitCalled = true }
|
||||
|
||||
t.Cleanup(func() {
|
||||
output = originalOutput
|
||||
exit = originalExit
|
||||
*versionFlag = versionValue(initialFlagValue)
|
||||
err := version.SetDynamicVersion(initialVersion.GitVersion)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
AddFlags(fs)
|
||||
err := fs.Parse(tc.flags)
|
||||
if tc.expectError != "" {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.expectError) {
|
||||
t.Fatalf("expected error containing %q, got %q", tc.expectError, err.Error())
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected parse error: %v", err)
|
||||
}
|
||||
|
||||
if e, a := tc.expectGitVersion, version.Get().GitVersion; e != a {
|
||||
t.Fatalf("gitversion: expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
PrintAndExitIfRequested()
|
||||
if e, a := tc.expectExit, exitCalled; e != a {
|
||||
t.Fatalf("exit(): expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := tc.expectPrintVersion, strings.TrimSpace(outputBuffer.String()); e != a {
|
||||
t.Fatalf("print version: expected %v, got %v", e, a)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ func Get() apimachineryversion.Info {
|
||||
return apimachineryversion.Info{
|
||||
Major: gitMajor,
|
||||
Minor: gitMinor,
|
||||
GitVersion: gitVersion,
|
||||
GitVersion: dynamicGitVersion.Load().(string),
|
||||
GitCommit: gitCommit,
|
||||
GitTreeState: gitTreeState,
|
||||
BuildDate: buildDate,
|
||||
|
@ -30,15 +30,13 @@ import (
|
||||
"k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||
"k8s.io/apiserver/pkg/server/egressselector"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/pkg/version"
|
||||
"k8s.io/client-go/transport"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||
"k8s.io/component-base/version"
|
||||
v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
||||
v1helper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper"
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
|
||||
@ -52,6 +50,7 @@ import (
|
||||
openapiv3aggregator "k8s.io/kube-aggregator/pkg/controllers/openapiv3/aggregator"
|
||||
statuscontrollers "k8s.io/kube-aggregator/pkg/controllers/status"
|
||||
apiservicerest "k8s.io/kube-aggregator/pkg/registry/apiservice/rest"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
Loading…
Reference in New Issue
Block a user