Upgrade cluster-api to v1beta1 for v1alpha3/v1alpha4 deprecated
Signed-off-by: zhangdiandian <1635468471@qq.com>
This commit is contained in:
parent
b4cf61419b
commit
291ffdbcc1
|
@ -17688,11 +17688,6 @@
|
|||
"kind": "DeleteOptions",
|
||||
"version": "v1alpha1"
|
||||
},
|
||||
{
|
||||
"group": "cluster.x-k8s.io",
|
||||
"kind": "DeleteOptions",
|
||||
"version": "v1alpha4"
|
||||
},
|
||||
{
|
||||
"group": "cluster.x-k8s.io",
|
||||
"kind": "DeleteOptions",
|
||||
|
@ -18429,11 +18424,6 @@
|
|||
"kind": "WatchEvent",
|
||||
"version": "v1alpha1"
|
||||
},
|
||||
{
|
||||
"group": "cluster.x-k8s.io",
|
||||
"kind": "WatchEvent",
|
||||
"version": "v1alpha4"
|
||||
},
|
||||
{
|
||||
"group": "cluster.x-k8s.io",
|
||||
"kind": "WatchEvent",
|
||||
|
|
1
go.mod
1
go.mod
|
@ -87,7 +87,6 @@ require (
|
|||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gobuffalo/flect v1.0.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -324,8 +324,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
|||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
||||
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/klog/v2"
|
||||
clusterapiv1alpha4 "sigs.k8s.io/cluster-api/api/v1alpha4" //nolint:staticcheck // disable `deprecation` check for backward compatibility.
|
||||
clusterapiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
secretutil "sigs.k8s.io/cluster-api/util/secret"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
@ -36,7 +35,6 @@ const (
|
|||
|
||||
var (
|
||||
clusterGVRs = []schema.GroupVersionResource{
|
||||
{Group: clusterapiv1alpha4.GroupVersion.Group, Version: clusterapiv1alpha4.GroupVersion.Version, Resource: resourceCluster},
|
||||
{Group: clusterapiv1beta1.GroupVersion.Group, Version: clusterapiv1beta1.GroupVersion.Version, Resource: resourceCluster},
|
||||
}
|
||||
)
|
||||
|
@ -129,7 +127,7 @@ func (d *ClusterDetector) Reconcile(key util.QueueKey) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if ok && clusterPhase == string(clusterapiv1alpha4.ClusterPhaseProvisioned) {
|
||||
if ok && clusterPhase == string(clusterapiv1beta1.ClusterPhaseProvisioned) {
|
||||
return d.joinClusterAPICluster(clusterWideKey)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
clusterapiv1alpha4 "sigs.k8s.io/cluster-api/api/v1alpha4" //nolint:staticcheck // disable `deprecation` check for backward compatibility.
|
||||
clusterapiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
mcsv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
|
||||
|
@ -33,7 +32,6 @@ func init() {
|
|||
utilruntime.Must(workv1alpha2.AddToScheme(aggregatedScheme)) // add work v1alpha2 schemes
|
||||
utilruntime.Must(searchv1alpha1.AddToScheme(aggregatedScheme)) // add search v1alpha1 schemes
|
||||
utilruntime.Must(mcsv1alpha1.AddToScheme(aggregatedScheme)) // add mcs-api schemes
|
||||
utilruntime.Must(clusterapiv1alpha4.AddToScheme(aggregatedScheme)) // add cluster-api v1alpha4 schemes
|
||||
utilruntime.Must(clusterapiv1beta1.AddToScheme(aggregatedScheme)) // add cluster-api v1beta1 schemes
|
||||
utilruntime.Must(autoscalingv1alpha1.AddToScheme(aggregatedScheme)) // add autoscaling v1alpha1 schemes
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
*.log
|
||||
.DS_Store
|
||||
doc
|
||||
tmp
|
||||
pkg
|
||||
*.gem
|
||||
*.pid
|
||||
coverage
|
||||
coverage.data
|
||||
build/*
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
.svn
|
||||
profile
|
||||
.console_history
|
||||
.sass-cache/*
|
||||
.rake_tasks~
|
||||
*.log.lck
|
||||
solr/
|
||||
.jhw-cache/
|
||||
jhw.*
|
||||
*.sublime*
|
||||
node_modules/
|
||||
dist/
|
||||
generated/
|
||||
.vendor/
|
||||
bin/*
|
||||
gin-bin
|
||||
.idea/
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"Enable": ["vet", "golint", "goimports", "deadcode", "gotype", "ineffassign", "misspell", "nakedret", "unconvert", "megacheck", "varcheck"]
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Mark Bates
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,61 +0,0 @@
|
|||
TAGS ?= ""
|
||||
GO_BIN ?= "go"
|
||||
|
||||
install:
|
||||
$(GO_BIN) install -tags ${TAGS} -v .
|
||||
make tidy
|
||||
|
||||
tidy:
|
||||
ifeq ($(GO111MODULE),on)
|
||||
$(GO_BIN) mod tidy
|
||||
else
|
||||
echo skipping go mod tidy
|
||||
endif
|
||||
|
||||
deps:
|
||||
$(GO_BIN) get -tags ${TAGS} -t ./...
|
||||
make tidy
|
||||
|
||||
build:
|
||||
$(GO_BIN) build -v .
|
||||
make tidy
|
||||
|
||||
test:
|
||||
$(GO_BIN) test -cover -tags ${TAGS} ./...
|
||||
make tidy
|
||||
|
||||
ci-deps:
|
||||
$(GO_BIN) get -tags ${TAGS} -t ./...
|
||||
|
||||
ci-test:
|
||||
$(GO_BIN) test -tags ${TAGS} -race ./...
|
||||
|
||||
lint:
|
||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
golangci-lint run --enable-all
|
||||
make tidy
|
||||
|
||||
update:
|
||||
ifeq ($(GO111MODULE),on)
|
||||
rm go.*
|
||||
$(GO_BIN) mod init
|
||||
$(GO_BIN) mod tidy
|
||||
else
|
||||
$(GO_BIN) get -u -tags ${TAGS}
|
||||
endif
|
||||
make test
|
||||
make install
|
||||
make tidy
|
||||
|
||||
release-test:
|
||||
$(GO_BIN) test -tags ${TAGS} -race ./...
|
||||
make tidy
|
||||
|
||||
release:
|
||||
$(GO_BIN) get github.com/gobuffalo/release
|
||||
make tidy
|
||||
release -y -f version.go --skip-packr
|
||||
make tidy
|
||||
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# Flect
|
||||
|
||||
[](https://pkg.go.dev/github.com/gobuffalo/flect)
|
||||
[](https://github.com/gobuffalo/flect/actions/workflows/standard-go-test.yml)
|
||||
[](https://goreportcard.com/report/github.com/gobuffalo/flect)
|
||||
|
||||
This is a new inflection engine to replace [https://github.com/markbates/inflect](https://github.com/markbates/inflect) designed to be more modular, more readable, and easier to fix issues on than the original.
|
||||
|
||||
Flect provides word inflection features such as `Singularize` and `Pluralize`
|
||||
for English nouns and text utility features such as `Camelize`, `Capitalize`,
|
||||
`Humanize`, and more.
|
||||
|
||||
Due to the flexibly-complex nature of English noun inflection, it is almost
|
||||
impossible to cover all exceptions (such as identical/irregular plural).
|
||||
With this reason along with the main purpose of Flect, which is to make it
|
||||
easy to develop web application in Go, Flect has limitations with its own
|
||||
rules.
|
||||
|
||||
* It covers regular rule (adding -s or -es and of the word)
|
||||
* It covers well-known irregular rules (such as -is to -es, -f to -ves, etc)
|
||||
* https://en.wiktionary.org/wiki/Appendix:English_irregular_nouns#Rules
|
||||
* It covers well-known irregular words (such as children, men, etc)
|
||||
* If a word can be countable and uncountable like milk or time, it will be
|
||||
treated as countable.
|
||||
* If a word has more than one plural forms, which means it has at least one
|
||||
irregular plural, we tried to find most popular one. (The selected plural
|
||||
could be odd to you, please feel free to open an issue with back data)
|
||||
* For example, we selected "stadiums" over "stadia", "dwarfs" over "dwarves"
|
||||
* One or combination of en.wiktionary.org, britannica.com, and
|
||||
trends.google.com are used to check the recent usage trends.
|
||||
* However, we cannot cover all cases and some of our cases could not fit with
|
||||
your situation. You can override the default with functions such as
|
||||
`InsertPlural()`, `InsertSingular()`, or `LoadInfrections()`.
|
||||
* If you have a json file named `inflections.json` in your application root,
|
||||
the file will be automatically loaded as your custom inflection dictionary.
|
||||
|
||||
## Installation
|
||||
|
||||
```console
|
||||
$ go get github.com/gobuffalo/flect
|
||||
```
|
||||
|
||||
|
||||
## Packages
|
||||
|
||||
### `github.com/gobuffalo/flect`
|
||||
|
||||
The `github.com/gobuffalo/flect` package contains "basic" inflection tools, like pluralization, singularization, etc...
|
||||
|
||||
#### The `Ident` Type
|
||||
|
||||
In addition to helpful methods that take in a `string` and return a `string`, there is an `Ident` type that can be used to create new, custom, inflection rules.
|
||||
|
||||
The `Ident` type contains two fields.
|
||||
|
||||
* `Original` - This is the original `string` that was used to create the `Ident`
|
||||
* `Parts` - This is a `[]string` that represents all of the "parts" of the string, that have been split apart, making the segments easier to work with
|
||||
|
||||
Examples of creating new inflection rules using `Ident` can be found in the `github.com/gobuffalo/flect/name` package.
|
||||
|
||||
### `github.com/gobuffalo/flect/name`
|
||||
|
||||
The `github.com/gobuffalo/flect/name` package contains more "business" inflection rules like creating proper names, table names, etc...
|
|
@ -1,12 +0,0 @@
|
|||
# Flect Stands on the Shoulders of Giants
|
||||
|
||||
Flect does not try to reinvent the wheel! Instead, it uses the already great wheels developed by the Go community and puts them all together in the best way possible. Without these giants, this project would not be possible. Please make sure to check them out and thank them for all of their hard work.
|
||||
|
||||
Thank you to the following **GIANTS**:
|
||||
|
||||
* [github.com/davecgh/go-spew](https://godoc.org/github.com/davecgh/go-spew)
|
||||
* [github.com/pmezard/go-difflib](https://godoc.org/github.com/pmezard/go-difflib)
|
||||
* [github.com/stretchr/objx](https://godoc.org/github.com/stretchr/objx)
|
||||
* [github.com/stretchr/testify](https://godoc.org/github.com/stretchr/testify)
|
||||
* [gopkg.in/check.v1](https://godoc.org/gopkg.in/check.v1)
|
||||
* [gopkg.in/yaml.v3](https://godoc.org/gopkg.in/yaml.v3)
|
|
@ -1,152 +0,0 @@
|
|||
package flect
|
||||
|
||||
import "sync"
|
||||
|
||||
var acronymsMoot = &sync.RWMutex{}
|
||||
|
||||
var baseAcronyms = map[string]bool{
|
||||
"OK": true,
|
||||
"UTF8": true,
|
||||
"HTML": true,
|
||||
"JSON": true,
|
||||
"JWT": true,
|
||||
"ID": true,
|
||||
"UUID": true,
|
||||
"SQL": true,
|
||||
"ACK": true,
|
||||
"ACL": true,
|
||||
"ADSL": true,
|
||||
"AES": true,
|
||||
"ANSI": true,
|
||||
"API": true,
|
||||
"ARP": true,
|
||||
"ATM": true,
|
||||
"BGP": true,
|
||||
"BSS": true,
|
||||
"CCITT": true,
|
||||
"CHAP": true,
|
||||
"CIDR": true,
|
||||
"CIR": true,
|
||||
"CLI": true,
|
||||
"CPE": true,
|
||||
"CPU": true,
|
||||
"CRC": true,
|
||||
"CRT": true,
|
||||
"CSMA": true,
|
||||
"CMOS": true,
|
||||
"DCE": true,
|
||||
"DEC": true,
|
||||
"DES": true,
|
||||
"DHCP": true,
|
||||
"DNS": true,
|
||||
"DRAM": true,
|
||||
"DSL": true,
|
||||
"DSLAM": true,
|
||||
"DTE": true,
|
||||
"DMI": true,
|
||||
"EHA": true,
|
||||
"EIA": true,
|
||||
"EIGRP": true,
|
||||
"EOF": true,
|
||||
"ESS": true,
|
||||
"FCC": true,
|
||||
"FCS": true,
|
||||
"FDDI": true,
|
||||
"FTP": true,
|
||||
"GBIC": true,
|
||||
"gbps": true,
|
||||
"GEPOF": true,
|
||||
"HDLC": true,
|
||||
"HTTP": true,
|
||||
"HTTPS": true,
|
||||
"IANA": true,
|
||||
"ICMP": true,
|
||||
"IDF": true,
|
||||
"IDS": true,
|
||||
"IEEE": true,
|
||||
"IETF": true,
|
||||
"IMAP": true,
|
||||
"IP": true,
|
||||
"IPS": true,
|
||||
"ISDN": true,
|
||||
"ISP": true,
|
||||
"kbps": true,
|
||||
"LACP": true,
|
||||
"LAN": true,
|
||||
"LAPB": true,
|
||||
"LAPF": true,
|
||||
"LLC": true,
|
||||
"MAC": true,
|
||||
"Mbps": true,
|
||||
"MC": true,
|
||||
"MDF": true,
|
||||
"MIB": true,
|
||||
"MoCA": true,
|
||||
"MPLS": true,
|
||||
"MTU": true,
|
||||
"NAC": true,
|
||||
"NAT": true,
|
||||
"NBMA": true,
|
||||
"NIC": true,
|
||||
"NRZ": true,
|
||||
"NRZI": true,
|
||||
"NVRAM": true,
|
||||
"OSI": true,
|
||||
"OSPF": true,
|
||||
"OUI": true,
|
||||
"PAP": true,
|
||||
"PAT": true,
|
||||
"PC": true,
|
||||
"PIM": true,
|
||||
"PCM": true,
|
||||
"PDU": true,
|
||||
"POP3": true,
|
||||
"POTS": true,
|
||||
"PPP": true,
|
||||
"PPTP": true,
|
||||
"PTT": true,
|
||||
"PVST": true,
|
||||
"RAM": true,
|
||||
"RARP": true,
|
||||
"RFC": true,
|
||||
"RIP": true,
|
||||
"RLL": true,
|
||||
"ROM": true,
|
||||
"RSTP": true,
|
||||
"RTP": true,
|
||||
"RCP": true,
|
||||
"SDLC": true,
|
||||
"SFD": true,
|
||||
"SFP": true,
|
||||
"SLARP": true,
|
||||
"SLIP": true,
|
||||
"SMTP": true,
|
||||
"SNA": true,
|
||||
"SNAP": true,
|
||||
"SNMP": true,
|
||||
"SOF": true,
|
||||
"SRAM": true,
|
||||
"SSH": true,
|
||||
"SSID": true,
|
||||
"STP": true,
|
||||
"SYN": true,
|
||||
"TDM": true,
|
||||
"TFTP": true,
|
||||
"TIA": true,
|
||||
"TOFU": true,
|
||||
"UDP": true,
|
||||
"URL": true,
|
||||
"URI": true,
|
||||
"USB": true,
|
||||
"UTP": true,
|
||||
"VC": true,
|
||||
"VLAN": true,
|
||||
"VLSM": true,
|
||||
"VPN": true,
|
||||
"W3C": true,
|
||||
"WAN": true,
|
||||
"WEP": true,
|
||||
"WiFi": true,
|
||||
"WPA": true,
|
||||
"WWW": true,
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Camelize returns a camelize version of a string
|
||||
// bob dylan = bobDylan
|
||||
// widget_id = widgetID
|
||||
// WidgetID = widgetID
|
||||
func Camelize(s string) string {
|
||||
return New(s).Camelize().String()
|
||||
}
|
||||
|
||||
// Camelize returns a camelize version of a string
|
||||
// bob dylan = bobDylan
|
||||
// widget_id = widgetID
|
||||
// WidgetID = widgetID
|
||||
func (i Ident) Camelize() Ident {
|
||||
var out []string
|
||||
for i, part := range i.Parts {
|
||||
var x string
|
||||
var capped bool
|
||||
for _, c := range part {
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) {
|
||||
if i == 0 {
|
||||
x += string(unicode.ToLower(c))
|
||||
continue
|
||||
}
|
||||
if !capped {
|
||||
capped = true
|
||||
x += string(unicode.ToUpper(c))
|
||||
continue
|
||||
}
|
||||
x += string(c)
|
||||
}
|
||||
}
|
||||
if x != "" {
|
||||
out = append(out, x)
|
||||
}
|
||||
}
|
||||
return New(strings.Join(out, ""))
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package flect
|
||||
|
||||
import "unicode"
|
||||
|
||||
// Capitalize will cap the first letter of string
|
||||
// user = User
|
||||
// bob dylan = Bob dylan
|
||||
// widget_id = Widget_id
|
||||
func Capitalize(s string) string {
|
||||
return New(s).Capitalize().String()
|
||||
}
|
||||
|
||||
// Capitalize will cap the first letter of string
|
||||
// user = User
|
||||
// bob dylan = Bob dylan
|
||||
// widget_id = Widget_id
|
||||
func (i Ident) Capitalize() Ident {
|
||||
if len(i.Parts) == 0 {
|
||||
return New("")
|
||||
}
|
||||
runes := []rune(i.Original)
|
||||
runes[0] = unicode.ToTitle(runes[0])
|
||||
return New(string(runes))
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
loadCustomData("inflections.json", "INFLECT_PATH", "could not read inflection file", LoadInflections)
|
||||
loadCustomData("acronyms.json", "ACRONYMS_PATH", "could not read acronyms file", LoadAcronyms)
|
||||
}
|
||||
|
||||
//CustomDataParser are functions that parse data like acronyms or
|
||||
//plurals in the shape of a io.Reader it receives.
|
||||
type CustomDataParser func(io.Reader) error
|
||||
|
||||
func loadCustomData(defaultFile, env, readErrorMessage string, parser CustomDataParser) {
|
||||
pwd, _ := os.Getwd()
|
||||
path, found := os.LookupEnv(env)
|
||||
if !found {
|
||||
path = filepath.Join(pwd, defaultFile)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("%s %s (%s)\n", readErrorMessage, path, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = parser(bytes.NewReader(b)); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
//LoadAcronyms loads rules from io.Reader param
|
||||
func LoadAcronyms(r io.Reader) error {
|
||||
m := []string{}
|
||||
err := json.NewDecoder(r).Decode(&m)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode acronyms JSON from reader: %s", err)
|
||||
}
|
||||
|
||||
acronymsMoot.Lock()
|
||||
defer acronymsMoot.Unlock()
|
||||
|
||||
for _, acronym := range m {
|
||||
baseAcronyms[acronym] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//LoadInflections loads rules from io.Reader param
|
||||
func LoadInflections(r io.Reader) error {
|
||||
m := map[string]string{}
|
||||
|
||||
err := json.NewDecoder(r).Decode(&m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not decode inflection JSON from reader: %s", err)
|
||||
}
|
||||
|
||||
pluralMoot.Lock()
|
||||
defer pluralMoot.Unlock()
|
||||
singularMoot.Lock()
|
||||
defer singularMoot.Unlock()
|
||||
|
||||
for s, p := range m {
|
||||
if strings.Contains(s, " ") || strings.Contains(p, " ") {
|
||||
// flect works with parts, so multi-words should not be allowed
|
||||
return fmt.Errorf("inflection elements should be a single word")
|
||||
}
|
||||
singleToPlural[s] = p
|
||||
pluralToSingle[p] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Dasherize returns an alphanumeric, lowercased, dashed string
|
||||
// Donald E. Knuth = donald-e-knuth
|
||||
// Test with + sign = test-with-sign
|
||||
// admin/WidgetID = admin-widget-id
|
||||
func Dasherize(s string) string {
|
||||
return New(s).Dasherize().String()
|
||||
}
|
||||
|
||||
// Dasherize returns an alphanumeric, lowercased, dashed string
|
||||
// Donald E. Knuth = donald-e-knuth
|
||||
// Test with + sign = test-with-sign
|
||||
// admin/WidgetID = admin-widget-id
|
||||
func (i Ident) Dasherize() Ident {
|
||||
var parts []string
|
||||
|
||||
for _, part := range i.Parts {
|
||||
var x string
|
||||
for _, c := range part {
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) {
|
||||
x += string(c)
|
||||
}
|
||||
}
|
||||
parts = xappend(parts, x)
|
||||
}
|
||||
|
||||
return New(strings.ToLower(strings.Join(parts, "-")))
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
Package flect is a new inflection engine to replace [https://github.com/markbates/inflect](https://github.com/markbates/inflect) designed to be more modular, more readable, and easier to fix issues on than the original.
|
||||
*/
|
||||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var spaces = []rune{'_', ' ', ':', '-', '/'}
|
||||
|
||||
func isSpace(c rune) bool {
|
||||
for _, r := range spaces {
|
||||
if r == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return unicode.IsSpace(c)
|
||||
}
|
||||
|
||||
func xappend(a []string, ss ...string) []string {
|
||||
for _, s := range ss {
|
||||
s = strings.TrimSpace(s)
|
||||
for _, x := range spaces {
|
||||
s = strings.Trim(s, string(x))
|
||||
}
|
||||
if _, ok := baseAcronyms[strings.ToUpper(s)]; ok {
|
||||
s = strings.ToUpper(s)
|
||||
}
|
||||
if s != "" {
|
||||
a = append(a, s)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Humanize returns first letter of sentence capitalized.
|
||||
// Common acronyms are capitalized as well.
|
||||
// Other capital letters in string are left as provided.
|
||||
// employee_salary = Employee salary
|
||||
// employee_id = employee ID
|
||||
// employee_mobile_number = Employee mobile number
|
||||
// first_Name = First Name
|
||||
// firstName = First Name
|
||||
func Humanize(s string) string {
|
||||
return New(s).Humanize().String()
|
||||
}
|
||||
|
||||
// Humanize First letter of sentence capitalized
|
||||
func (i Ident) Humanize() Ident {
|
||||
if len(i.Original) == 0 {
|
||||
return New("")
|
||||
}
|
||||
|
||||
parts := xappend([]string{}, Titleize(i.Parts[0]))
|
||||
if len(i.Parts) > 1 {
|
||||
parts = xappend(parts, i.Parts[1:]...)
|
||||
}
|
||||
|
||||
return New(strings.Join(parts, " "))
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Ident represents the string and it's parts
|
||||
type Ident struct {
|
||||
Original string
|
||||
Parts []string
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer and returns the original string
|
||||
func (i Ident) String() string {
|
||||
return i.Original
|
||||
}
|
||||
|
||||
// New creates a new Ident from the string
|
||||
func New(s string) Ident {
|
||||
i := Ident{
|
||||
Original: s,
|
||||
Parts: toParts(s),
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func toParts(s string) []string {
|
||||
parts := []string{}
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) == 0 {
|
||||
return parts
|
||||
}
|
||||
if _, ok := baseAcronyms[strings.ToUpper(s)]; ok {
|
||||
return []string{strings.ToUpper(s)}
|
||||
}
|
||||
var prev rune
|
||||
var x strings.Builder
|
||||
x.Grow(len(s))
|
||||
for _, c := range s {
|
||||
// fmt.Println("### cs ->", cs)
|
||||
// fmt.Println("### unicode.IsControl(c) ->", unicode.IsControl(c))
|
||||
// fmt.Println("### unicode.IsDigit(c) ->", unicode.IsDigit(c))
|
||||
// fmt.Println("### unicode.IsGraphic(c) ->", unicode.IsGraphic(c))
|
||||
// fmt.Println("### unicode.IsLetter(c) ->", unicode.IsLetter(c))
|
||||
// fmt.Println("### unicode.IsLower(c) ->", unicode.IsLower(c))
|
||||
// fmt.Println("### unicode.IsMark(c) ->", unicode.IsMark(c))
|
||||
// fmt.Println("### unicode.IsPrint(c) ->", unicode.IsPrint(c))
|
||||
// fmt.Println("### unicode.IsPunct(c) ->", unicode.IsPunct(c))
|
||||
// fmt.Println("### unicode.IsSpace(c) ->", unicode.IsSpace(c))
|
||||
// fmt.Println("### unicode.IsTitle(c) ->", unicode.IsTitle(c))
|
||||
// fmt.Println("### unicode.IsUpper(c) ->", unicode.IsUpper(c))
|
||||
if !utf8.ValidRune(c) {
|
||||
continue
|
||||
}
|
||||
|
||||
if isSpace(c) {
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
|
||||
if unicode.IsUpper(c) && !unicode.IsUpper(prev) {
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
if unicode.IsUpper(c) && baseAcronyms[strings.ToUpper(x.String())] {
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
x.WriteRune(c)
|
||||
prev = c
|
||||
continue
|
||||
}
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) || unicode.IsPunct(c) || c == '`' {
|
||||
prev = c
|
||||
x.WriteRune(c)
|
||||
continue
|
||||
}
|
||||
|
||||
parts = xappend(parts, x.String())
|
||||
x.Reset()
|
||||
prev = c
|
||||
}
|
||||
parts = xappend(parts, x.String())
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
var _ encoding.TextUnmarshaler = &Ident{}
|
||||
var _ encoding.TextMarshaler = &Ident{}
|
||||
|
||||
// LastPart returns the last part/word of the original string
|
||||
func (i *Ident) LastPart() string {
|
||||
if len(i.Parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
return i.Parts[len(i.Parts)-1]
|
||||
}
|
||||
|
||||
// ReplaceSuffix creates a new Ident with the original suffix replaced by new
|
||||
func (i Ident) ReplaceSuffix(orig, new string) Ident {
|
||||
return New(strings.TrimSuffix(i.Original, orig) + new)
|
||||
}
|
||||
|
||||
//UnmarshalText unmarshalls byte array into the Ident
|
||||
func (i *Ident) UnmarshalText(data []byte) error {
|
||||
(*i) = New(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
//MarshalText marshals Ident into byte array
|
||||
func (i Ident) MarshalText() ([]byte, error) {
|
||||
return []byte(i.Original), nil
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package flect
|
||||
|
||||
import "strings"
|
||||
|
||||
// ToUpper is a convience wrapper for strings.ToUpper
|
||||
func (i Ident) ToUpper() Ident {
|
||||
return New(strings.ToUpper(i.Original))
|
||||
}
|
||||
|
||||
// ToLower is a convience wrapper for strings.ToLower
|
||||
func (i Ident) ToLower() Ident {
|
||||
return New(strings.ToLower(i.Original))
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Ordinalize converts a number to an ordinal version
|
||||
// 42 = 42nd
|
||||
// 45 = 45th
|
||||
// 1 = 1st
|
||||
func Ordinalize(s string) string {
|
||||
return New(s).Ordinalize().String()
|
||||
}
|
||||
|
||||
// Ordinalize converts a number to an ordinal version
|
||||
// 42 = 42nd
|
||||
// 45 = 45th
|
||||
// 1 = 1st
|
||||
func (i Ident) Ordinalize() Ident {
|
||||
number, err := strconv.Atoi(i.Original)
|
||||
if err != nil {
|
||||
return i
|
||||
}
|
||||
var s string
|
||||
switch abs(number) % 100 {
|
||||
case 11, 12, 13:
|
||||
s = fmt.Sprintf("%dth", number)
|
||||
default:
|
||||
switch abs(number) % 10 {
|
||||
case 1:
|
||||
s = fmt.Sprintf("%dst", number)
|
||||
case 2:
|
||||
s = fmt.Sprintf("%dnd", number)
|
||||
case 3:
|
||||
s = fmt.Sprintf("%drd", number)
|
||||
}
|
||||
}
|
||||
if s != "" {
|
||||
return New(s)
|
||||
}
|
||||
return New(fmt.Sprintf("%dth", number))
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pascalize returns a string with each segment capitalized
|
||||
// user = User
|
||||
// bob dylan = BobDylan
|
||||
// widget_id = WidgetID
|
||||
func Pascalize(s string) string {
|
||||
return New(s).Pascalize().String()
|
||||
}
|
||||
|
||||
// Pascalize returns a string with each segment capitalized
|
||||
// user = User
|
||||
// bob dylan = BobDylan
|
||||
// widget_id = WidgetID
|
||||
func (i Ident) Pascalize() Ident {
|
||||
c := i.Camelize()
|
||||
if len(c.String()) == 0 {
|
||||
return c
|
||||
}
|
||||
if len(i.Parts) == 0 {
|
||||
return i
|
||||
}
|
||||
capLen := 1
|
||||
if _, ok := baseAcronyms[strings.ToUpper(i.Parts[0])]; ok {
|
||||
capLen = len(i.Parts[0])
|
||||
}
|
||||
return New(string(strings.ToUpper(c.Original[0:capLen])) + c.Original[capLen:])
|
||||
}
|
|
@ -1,417 +0,0 @@
|
|||
package flect
|
||||
|
||||
import "fmt"
|
||||
|
||||
var pluralRules = []rule{}
|
||||
|
||||
// AddPlural adds a rule that will replace the given suffix with the replacement suffix.
|
||||
// The name is confusing. This function will be deprecated in the next release.
|
||||
func AddPlural(suffix string, repl string) {
|
||||
InsertPluralRule(suffix, repl)
|
||||
}
|
||||
|
||||
// InsertPluralRule inserts a rule that will replace the given suffix with
|
||||
// the repl(acement) at the begining of the list of the pluralize rules.
|
||||
func InsertPluralRule(suffix, repl string) {
|
||||
pluralMoot.Lock()
|
||||
defer pluralMoot.Unlock()
|
||||
|
||||
pluralRules = append([]rule{{
|
||||
suffix: suffix,
|
||||
fn: simpleRuleFunc(suffix, repl),
|
||||
}}, pluralRules...)
|
||||
|
||||
pluralRules = append([]rule{{
|
||||
suffix: repl,
|
||||
fn: noop,
|
||||
}}, pluralRules...)
|
||||
}
|
||||
|
||||
type word struct {
|
||||
singular string
|
||||
plural string
|
||||
alternative string
|
||||
unidirectional bool // plural to singular is not possible (or bad)
|
||||
uncountable bool
|
||||
exact bool
|
||||
}
|
||||
|
||||
// dictionary is the main table for singularize and pluralize.
|
||||
// All words in the dictionary will be added to singleToPlural, pluralToSingle
|
||||
// and singlePluralAssertions by init() functions.
|
||||
var dictionary = []word{
|
||||
// identicals https://en.wikipedia.org/wiki/English_plurals#Nouns_with_identical_singular_and_plural
|
||||
{singular: "aircraft", plural: "aircraft"},
|
||||
{singular: "beef", plural: "beef", alternative: "beefs"},
|
||||
{singular: "bison", plural: "bison"},
|
||||
{singular: "blues", plural: "blues", unidirectional: true},
|
||||
{singular: "chassis", plural: "chassis"},
|
||||
{singular: "deer", plural: "deer"},
|
||||
{singular: "fish", plural: "fish", alternative: "fishes"},
|
||||
{singular: "moose", plural: "moose"},
|
||||
{singular: "police", plural: "police"},
|
||||
{singular: "salmon", plural: "salmon", alternative: "salmons"},
|
||||
{singular: "series", plural: "series"},
|
||||
{singular: "sheep", plural: "sheep"},
|
||||
{singular: "shrimp", plural: "shrimp", alternative: "shrimps"},
|
||||
{singular: "species", plural: "species"},
|
||||
{singular: "swine", plural: "swine", alternative: "swines"},
|
||||
{singular: "trout", plural: "trout", alternative: "trouts"},
|
||||
{singular: "tuna", plural: "tuna", alternative: "tunas"},
|
||||
{singular: "you", plural: "you"},
|
||||
// -en https://en.wikipedia.org/wiki/English_plurals#Plurals_in_-(e)n
|
||||
{singular: "child", plural: "children"},
|
||||
{singular: "ox", plural: "oxen", exact: true},
|
||||
// apophonic https://en.wikipedia.org/wiki/English_plurals#Apophonic_plurals
|
||||
{singular: "foot", plural: "feet"},
|
||||
{singular: "goose", plural: "geese"},
|
||||
{singular: "man", plural: "men"},
|
||||
{singular: "human", plural: "humans"}, // not humen
|
||||
{singular: "louse", plural: "lice", exact: true},
|
||||
{singular: "mouse", plural: "mice"},
|
||||
{singular: "tooth", plural: "teeth"},
|
||||
{singular: "woman", plural: "women"},
|
||||
// misc https://en.wikipedia.org/wiki/English_plurals#Miscellaneous_irregular_plurals
|
||||
{singular: "die", plural: "dice", exact: true},
|
||||
{singular: "person", plural: "people"},
|
||||
|
||||
// Words from French that end in -u add an x; in addition to eau to eaux rule
|
||||
{singular: "adieu", plural: "adieux", alternative: "adieus"},
|
||||
{singular: "fabliau", plural: "fabliaux"},
|
||||
{singular: "bureau", plural: "bureaus", alternative: "bureaux"}, // popular
|
||||
|
||||
// Words from Greek that end in -on change -on to -a; in addition to hedron rule
|
||||
{singular: "criterion", plural: "criteria"},
|
||||
{singular: "ganglion", plural: "ganglia", alternative: "ganglions"},
|
||||
{singular: "lexicon", plural: "lexica", alternative: "lexicons"},
|
||||
{singular: "mitochondrion", plural: "mitochondria", alternative: "mitochondrions"},
|
||||
{singular: "noumenon", plural: "noumena"},
|
||||
{singular: "phenomenon", plural: "phenomena"},
|
||||
{singular: "taxon", plural: "taxa"},
|
||||
|
||||
// Words from Latin that end in -um change -um to -a; in addition to some rules
|
||||
{singular: "media", plural: "media"}, // popular case: media -> media
|
||||
{singular: "medium", plural: "media", alternative: "mediums", unidirectional: true},
|
||||
{singular: "stadium", plural: "stadiums", alternative: "stadia"},
|
||||
{singular: "aquarium", plural: "aquaria", alternative: "aquariums"},
|
||||
{singular: "auditorium", plural: "auditoria", alternative: "auditoriums"},
|
||||
{singular: "symposium", plural: "symposia", alternative: "symposiums"},
|
||||
{singular: "curriculum", plural: "curriculums", alternative: "curricula"}, // ulum
|
||||
{singular: "quota", plural: "quotas"},
|
||||
|
||||
// Words from Latin that end in -us change -us to -i or -era
|
||||
{singular: "alumnus", plural: "alumni", alternative: "alumnuses"}, // -i
|
||||
{singular: "bacillus", plural: "bacilli"},
|
||||
{singular: "cactus", plural: "cacti", alternative: "cactuses"},
|
||||
{singular: "coccus", plural: "cocci"},
|
||||
{singular: "focus", plural: "foci", alternative: "focuses"},
|
||||
{singular: "locus", plural: "loci", alternative: "locuses"},
|
||||
{singular: "nucleus", plural: "nuclei", alternative: "nucleuses"},
|
||||
{singular: "octopus", plural: "octupuses", alternative: "octopi"},
|
||||
{singular: "radius", plural: "radii", alternative: "radiuses"},
|
||||
{singular: "syllabus", plural: "syllabi"},
|
||||
{singular: "corpus", plural: "corpora", alternative: "corpuses"}, // -ra
|
||||
{singular: "genus", plural: "genera"},
|
||||
|
||||
// Words from Latin that end in -a change -a to -ae
|
||||
{singular: "alumna", plural: "alumnae"},
|
||||
{singular: "vertebra", plural: "vertebrae"},
|
||||
{singular: "differentia", plural: "differentiae"}, // -tia
|
||||
{singular: "minutia", plural: "minutiae"},
|
||||
{singular: "vita", plural: "vitae"}, // -ita
|
||||
{singular: "larva", plural: "larvae"}, // -va
|
||||
{singular: "postcava", plural: "postcavae"},
|
||||
{singular: "praecava", plural: "praecavae"},
|
||||
{singular: "uva", plural: "uvae"},
|
||||
|
||||
// Words from Latin that end in -ex change -ex to -ices
|
||||
{singular: "apex", plural: "apices", alternative: "apexes"},
|
||||
{singular: "codex", plural: "codices", alternative: "codexes"},
|
||||
{singular: "index", plural: "indices", alternative: "indexes"},
|
||||
{singular: "latex", plural: "latices", alternative: "latexes"},
|
||||
{singular: "vertex", plural: "vertices", alternative: "vertexes"},
|
||||
{singular: "vortex", plural: "vortices", alternative: "vortexes"},
|
||||
|
||||
// Words from Latin that end in -ix change -ix to -ices (eg, matrix becomes matrices)
|
||||
{singular: "appendix", plural: "appendices", alternative: "appendixes"},
|
||||
{singular: "radix", plural: "radices", alternative: "radixes"},
|
||||
{singular: "helix", plural: "helices", alternative: "helixes"},
|
||||
|
||||
// Words from Latin that end in -is change -is to -es
|
||||
{singular: "axis", plural: "axes", exact: true},
|
||||
{singular: "crisis", plural: "crises"},
|
||||
{singular: "ellipsis", plural: "ellipses", unidirectional: true}, // ellipse
|
||||
{singular: "genesis", plural: "geneses"},
|
||||
{singular: "oasis", plural: "oases"},
|
||||
{singular: "thesis", plural: "theses"},
|
||||
{singular: "testis", plural: "testes"},
|
||||
{singular: "base", plural: "bases"}, // popular case
|
||||
{singular: "basis", plural: "bases", unidirectional: true},
|
||||
|
||||
{singular: "alias", plural: "aliases", exact: true}, // no alia, no aliasis
|
||||
{singular: "vedalia", plural: "vedalias"}, // no vedalium, no vedaliases
|
||||
|
||||
// Words that end in -ch, -o, -s, -sh, -x, -z (can be conflict with the others)
|
||||
{singular: "use", plural: "uses", exact: true}, // us vs use
|
||||
{singular: "abuse", plural: "abuses"},
|
||||
{singular: "cause", plural: "causes"},
|
||||
{singular: "clause", plural: "clauses"},
|
||||
{singular: "cruse", plural: "cruses"},
|
||||
{singular: "excuse", plural: "excuses"},
|
||||
{singular: "fuse", plural: "fuses"},
|
||||
{singular: "house", plural: "houses"},
|
||||
{singular: "misuse", plural: "misuses"},
|
||||
{singular: "muse", plural: "muses"},
|
||||
{singular: "pause", plural: "pauses"},
|
||||
{singular: "ache", plural: "aches"},
|
||||
{singular: "topaz", plural: "topazes"},
|
||||
{singular: "buffalo", plural: "buffaloes", alternative: "buffalos"},
|
||||
{singular: "potato", plural: "potatoes"},
|
||||
{singular: "tomato", plural: "tomatoes"},
|
||||
|
||||
// uncountables
|
||||
{singular: "equipment", uncountable: true},
|
||||
{singular: "information", uncountable: true},
|
||||
{singular: "jeans", uncountable: true},
|
||||
{singular: "money", uncountable: true},
|
||||
{singular: "news", uncountable: true},
|
||||
{singular: "rice", uncountable: true},
|
||||
|
||||
// exceptions: -f to -ves, not -fe
|
||||
{singular: "dwarf", plural: "dwarfs", alternative: "dwarves"},
|
||||
{singular: "hoof", plural: "hoofs", alternative: "hooves"},
|
||||
{singular: "thief", plural: "thieves"},
|
||||
// exceptions: instead of -f(e) to -ves
|
||||
{singular: "chive", plural: "chives"},
|
||||
{singular: "hive", plural: "hives"},
|
||||
{singular: "move", plural: "moves"},
|
||||
|
||||
// exceptions: instead of -y to -ies
|
||||
{singular: "movie", plural: "movies"},
|
||||
{singular: "cookie", plural: "cookies"},
|
||||
|
||||
// exceptions: instead of -um to -a
|
||||
{singular: "pretorium", plural: "pretoriums"},
|
||||
{singular: "agenda", plural: "agendas"}, // instead of plural of agendum
|
||||
// exceptions: instead of -um to -a (chemical element names)
|
||||
|
||||
// Words from Latin that end in -a change -a to -ae
|
||||
{singular: "formula", plural: "formulas", alternative: "formulae"}, // also -um/-a
|
||||
|
||||
// exceptions: instead of -o to -oes
|
||||
{singular: "shoe", plural: "shoes"},
|
||||
{singular: "toe", plural: "toes", exact: true},
|
||||
{singular: "graffiti", plural: "graffiti"},
|
||||
|
||||
// abbreviations
|
||||
{singular: "ID", plural: "IDs", exact: true},
|
||||
}
|
||||
|
||||
// singleToPlural is the highest priority map for Pluralize().
|
||||
// singularToPluralSuffixList is used to build pluralRules for suffixes and
|
||||
// compound words.
|
||||
var singleToPlural = map[string]string{}
|
||||
|
||||
// pluralToSingle is the highest priority map for Singularize().
|
||||
// singularToPluralSuffixList is used to build singularRules for suffixes and
|
||||
// compound words.
|
||||
var pluralToSingle = map[string]string{}
|
||||
|
||||
// NOTE: This map should not be built as reverse map of singleToPlural since
|
||||
// there are words that has the same plurals.
|
||||
|
||||
// build singleToPlural and pluralToSingle with dictionary
|
||||
func init() {
|
||||
for _, wd := range dictionary {
|
||||
if singleToPlural[wd.singular] != "" {
|
||||
panic(fmt.Errorf("map singleToPlural already has an entry for %s", wd.singular))
|
||||
}
|
||||
|
||||
if wd.uncountable && wd.plural == "" {
|
||||
wd.plural = wd.singular
|
||||
}
|
||||
|
||||
if wd.plural == "" {
|
||||
panic(fmt.Errorf("plural for %s is not provided", wd.singular))
|
||||
}
|
||||
|
||||
singleToPlural[wd.singular] = wd.plural
|
||||
|
||||
if !wd.unidirectional {
|
||||
if pluralToSingle[wd.plural] != "" {
|
||||
panic(fmt.Errorf("map pluralToSingle already has an entry for %s", wd.plural))
|
||||
}
|
||||
pluralToSingle[wd.plural] = wd.singular
|
||||
|
||||
if wd.alternative != "" {
|
||||
if pluralToSingle[wd.alternative] != "" {
|
||||
panic(fmt.Errorf("map pluralToSingle already has an entry for %s", wd.alternative))
|
||||
}
|
||||
pluralToSingle[wd.alternative] = wd.singular
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type singularToPluralSuffix struct {
|
||||
singular string
|
||||
plural string
|
||||
}
|
||||
|
||||
// singularToPluralSuffixList is a list of "bidirectional" suffix rules for
|
||||
// the irregular plurals follow such rules.
|
||||
//
|
||||
// NOTE: IMPORTANT! The order of items in this list is the rule priority, not
|
||||
// alphabet order. The first match will be used to inflect.
|
||||
var singularToPluralSuffixList = []singularToPluralSuffix{
|
||||
// https://en.wiktionary.org/wiki/Appendix:English_irregular_nouns#Rules
|
||||
// Words that end in -f or -fe change -f or -fe to -ves
|
||||
{"tive", "tives"}, // exception
|
||||
{"eaf", "eaves"},
|
||||
{"oaf", "oaves"},
|
||||
{"afe", "aves"},
|
||||
{"arf", "arves"},
|
||||
{"rfe", "rves"},
|
||||
{"rf", "rves"},
|
||||
{"lf", "lves"},
|
||||
{"fe", "ves"}, // previously '[a-eg-km-z]fe' TODO: regex support
|
||||
|
||||
// Words that end in -y preceded by a consonant change -y to -ies
|
||||
{"ay", "ays"},
|
||||
{"ey", "eys"},
|
||||
{"oy", "oys"},
|
||||
{"quy", "quies"},
|
||||
{"uy", "uys"},
|
||||
{"y", "ies"}, // '[^aeiou]y'
|
||||
|
||||
// Words from French that end in -u add an x (eg, château becomes châteaux)
|
||||
{"eau", "eaux"}, // it seems like 'eau' is the most popular form of this rule
|
||||
|
||||
// Words from Latin that end in -a change -a to -ae; before -on to -a and -um to -a
|
||||
{"bula", "bulae"},
|
||||
{"dula", "bulae"},
|
||||
{"lula", "bulae"},
|
||||
{"nula", "bulae"},
|
||||
{"vula", "bulae"},
|
||||
|
||||
// Words from Greek that end in -on change -on to -a (eg, polyhedron becomes polyhedra)
|
||||
// https://en.wiktionary.org/wiki/Category:English_irregular_plurals_ending_in_"-a"
|
||||
{"hedron", "hedra"},
|
||||
|
||||
// Words from Latin that end in -um change -um to -a (eg, minimum becomes minima)
|
||||
// https://en.wiktionary.org/wiki/Category:English_irregular_plurals_ending_in_"-a"
|
||||
{"ium", "ia"}, // some exceptions especially chemical element names
|
||||
{"seum", "seums"},
|
||||
{"eum", "ea"},
|
||||
{"oum", "oa"},
|
||||
{"stracum", "straca"},
|
||||
{"dum", "da"},
|
||||
{"elum", "ela"},
|
||||
{"ilum", "ila"},
|
||||
{"olum", "ola"},
|
||||
{"ulum", "ula"},
|
||||
{"llum", "lla"},
|
||||
{"ylum", "yla"},
|
||||
{"imum", "ima"},
|
||||
{"ernum", "erna"},
|
||||
{"gnum", "gna"},
|
||||
{"brum", "bra"},
|
||||
{"crum", "cra"},
|
||||
{"terum", "tera"},
|
||||
{"serum", "sera"},
|
||||
{"trum", "tra"},
|
||||
{"antum", "anta"},
|
||||
{"atum", "ata"},
|
||||
{"entum", "enta"},
|
||||
{"etum", "eta"},
|
||||
{"itum", "ita"},
|
||||
{"otum", "ota"},
|
||||
{"utum", "uta"},
|
||||
{"ctum", "cta"},
|
||||
{"ovum", "ova"},
|
||||
|
||||
// Words from Latin that end in -us change -us to -i or -era
|
||||
// not easy to make a simple rule. just add them all to the dictionary
|
||||
|
||||
// Words from Latin that end in -ex change -ex to -ices (eg, vortex becomes vortices)
|
||||
// Words from Latin that end in -ix change -ix to -ices (eg, matrix becomes matrices)
|
||||
// for example, -dix, -dex, and -dice will have the same plural form so
|
||||
// making a simple rule is not possible for them
|
||||
{"trix", "trices"}, // ignore a few words end in trice
|
||||
|
||||
// Words from Latin that end in -is change -is to -es (eg, thesis becomes theses)
|
||||
// -sis and -se has the same plural -ses so making a rule is not easy too.
|
||||
{"iasis", "iases"},
|
||||
{"mesis", "meses"},
|
||||
{"kinesis", "kineses"},
|
||||
{"resis", "reses"},
|
||||
{"gnosis", "gnoses"}, // e.g. diagnosis
|
||||
{"opsis", "opses"}, // e.g. synopsis
|
||||
{"ysis", "yses"}, // e.g. analysis
|
||||
|
||||
// Words that end in -ch, -o, -s, -sh, -x, -z
|
||||
{"ouse", "ouses"},
|
||||
{"lause", "lauses"},
|
||||
{"us", "uses"}, // use/uses is in the dictionary
|
||||
|
||||
{"ch", "ches"},
|
||||
{"io", "ios"},
|
||||
{"sh", "shes"},
|
||||
{"ss", "sses"},
|
||||
{"ez", "ezzes"},
|
||||
{"iz", "izzes"},
|
||||
{"tz", "tzes"},
|
||||
{"zz", "zzes"},
|
||||
{"ano", "anos"},
|
||||
{"lo", "los"},
|
||||
{"to", "tos"},
|
||||
{"oo", "oos"},
|
||||
{"o", "oes"},
|
||||
{"x", "xes"},
|
||||
|
||||
// for abbreviations
|
||||
{"S", "Ses"},
|
||||
|
||||
// excluded rules: seems rare
|
||||
// Words from Hebrew that add -im or -ot (eg, cherub becomes cherubim)
|
||||
// - cherub (cherubs or cherubim), seraph (seraphs or seraphim)
|
||||
// Words from Greek that end in -ma change -ma to -mata
|
||||
// - The most of words end in -ma are in this category but it looks like
|
||||
// just adding -s is more popular.
|
||||
// Words from Latin that end in -nx change -nx to -nges
|
||||
// - The most of words end in -nx are in this category but it looks like
|
||||
// just adding -es is more popular. (sphinxes)
|
||||
|
||||
// excluded rules: don't care at least for now:
|
||||
// Words that end in -ful that add an s after the -ful
|
||||
// Words that end in -s or -ese denoting a national of a particular country
|
||||
// Symbols or letters, which often add -'s
|
||||
}
|
||||
|
||||
func init() {
|
||||
for i := len(singularToPluralSuffixList) - 1; i >= 0; i-- {
|
||||
InsertPluralRule(singularToPluralSuffixList[i].singular, singularToPluralSuffixList[i].plural)
|
||||
InsertSingularRule(singularToPluralSuffixList[i].plural, singularToPluralSuffixList[i].singular)
|
||||
}
|
||||
|
||||
// build pluralRule and singularRule with dictionary for compound words
|
||||
for _, wd := range dictionary {
|
||||
if wd.exact {
|
||||
continue
|
||||
}
|
||||
|
||||
if wd.uncountable && wd.plural == "" {
|
||||
wd.plural = wd.singular
|
||||
}
|
||||
|
||||
InsertPluralRule(wd.singular, wd.plural)
|
||||
|
||||
if !wd.unidirectional {
|
||||
InsertSingularRule(wd.plural, wd.singular)
|
||||
|
||||
if wd.alternative != "" {
|
||||
InsertSingularRule(wd.alternative, wd.singular)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var pluralMoot = &sync.RWMutex{}
|
||||
|
||||
// Pluralize returns a plural version of the string
|
||||
// user = users
|
||||
// person = people
|
||||
// datum = data
|
||||
func Pluralize(s string) string {
|
||||
return New(s).Pluralize().String()
|
||||
}
|
||||
|
||||
// PluralizeWithSize will pluralize a string taking a number number into account.
|
||||
// PluralizeWithSize("user", 1) = user
|
||||
// PluralizeWithSize("user", 2) = users
|
||||
func PluralizeWithSize(s string, i int) string {
|
||||
if i == 1 || i == -1 {
|
||||
return New(s).Singularize().String()
|
||||
}
|
||||
return New(s).Pluralize().String()
|
||||
}
|
||||
|
||||
// Pluralize returns a plural version of the string
|
||||
// user = users
|
||||
// person = people
|
||||
// datum = data
|
||||
func (i Ident) Pluralize() Ident {
|
||||
s := i.LastPart()
|
||||
if len(s) == 0 {
|
||||
return New("")
|
||||
}
|
||||
|
||||
pluralMoot.RLock()
|
||||
defer pluralMoot.RUnlock()
|
||||
|
||||
// check if the Original has an explicit entry in the map
|
||||
if p, ok := singleToPlural[i.Original]; ok {
|
||||
return i.ReplaceSuffix(i.Original, p)
|
||||
}
|
||||
if _, ok := pluralToSingle[i.Original]; ok {
|
||||
return i
|
||||
}
|
||||
|
||||
ls := strings.ToLower(s)
|
||||
if _, ok := pluralToSingle[ls]; ok {
|
||||
return i
|
||||
}
|
||||
|
||||
if p, ok := singleToPlural[ls]; ok {
|
||||
if s == Capitalize(s) {
|
||||
p = Capitalize(p)
|
||||
}
|
||||
return i.ReplaceSuffix(s, p)
|
||||
}
|
||||
|
||||
for _, r := range pluralRules {
|
||||
if strings.HasSuffix(s, r.suffix) {
|
||||
return i.ReplaceSuffix(s, r.fn(s))
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(ls, "s") {
|
||||
return i
|
||||
}
|
||||
|
||||
return New(i.String() + "s")
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package flect
|
||||
|
||||
type ruleFn func(string) string
|
||||
|
||||
type rule struct {
|
||||
suffix string
|
||||
fn ruleFn
|
||||
}
|
||||
|
||||
func simpleRuleFunc(suffix, repl string) func(string) string {
|
||||
return func(s string) string {
|
||||
s = s[:len(s)-len(suffix)]
|
||||
return s + repl
|
||||
}
|
||||
}
|
||||
|
||||
func noop(s string) string { return s }
|
|
@ -1,26 +0,0 @@
|
|||
package flect
|
||||
|
||||
var singularRules = []rule{}
|
||||
|
||||
// AddSingular adds a rule that will replace the given suffix with the replacement suffix.
|
||||
// The name is confusing. This function will be deprecated in the next release.
|
||||
func AddSingular(ext string, repl string) {
|
||||
InsertSingularRule(ext, repl)
|
||||
}
|
||||
|
||||
// InsertSingularRule inserts a rule that will replace the given suffix with
|
||||
// the repl(acement) at the beginning of the list of the singularize rules.
|
||||
func InsertSingularRule(suffix, repl string) {
|
||||
singularMoot.Lock()
|
||||
defer singularMoot.Unlock()
|
||||
|
||||
singularRules = append([]rule{{
|
||||
suffix: suffix,
|
||||
fn: simpleRuleFunc(suffix, repl),
|
||||
}}, singularRules...)
|
||||
|
||||
singularRules = append([]rule{{
|
||||
suffix: repl,
|
||||
fn: noop,
|
||||
}}, singularRules...)
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var singularMoot = &sync.RWMutex{}
|
||||
|
||||
// Singularize returns a singular version of the string
|
||||
// users = user
|
||||
// data = datum
|
||||
// people = person
|
||||
func Singularize(s string) string {
|
||||
return New(s).Singularize().String()
|
||||
}
|
||||
|
||||
// SingularizeWithSize will singular a string taking a number number into account.
|
||||
// SingularizeWithSize("user", 1) = user
|
||||
// SingularizeWithSize("user", 2) = users
|
||||
func SingularizeWithSize(s string, i int) string {
|
||||
return PluralizeWithSize(s, i)
|
||||
}
|
||||
|
||||
// Singularize returns a singular version of the string
|
||||
// users = user
|
||||
// data = datum
|
||||
// people = person
|
||||
func (i Ident) Singularize() Ident {
|
||||
s := i.LastPart()
|
||||
if len(s) == 0 {
|
||||
return i
|
||||
}
|
||||
|
||||
singularMoot.RLock()
|
||||
defer singularMoot.RUnlock()
|
||||
|
||||
// check if the Original has an explicit entry in the map
|
||||
if p, ok := pluralToSingle[i.Original]; ok {
|
||||
return i.ReplaceSuffix(i.Original, p)
|
||||
}
|
||||
if _, ok := singleToPlural[i.Original]; ok {
|
||||
return i
|
||||
}
|
||||
|
||||
ls := strings.ToLower(s)
|
||||
if p, ok := pluralToSingle[ls]; ok {
|
||||
if s == Capitalize(s) {
|
||||
p = Capitalize(p)
|
||||
}
|
||||
return i.ReplaceSuffix(s, p)
|
||||
}
|
||||
|
||||
if _, ok := singleToPlural[ls]; ok {
|
||||
return i
|
||||
}
|
||||
|
||||
for _, r := range singularRules {
|
||||
if strings.HasSuffix(s, r.suffix) {
|
||||
return i.ReplaceSuffix(s, r.fn(s))
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(s, "s") {
|
||||
return i.ReplaceSuffix("s", "")
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Titleize will capitalize the start of each part
|
||||
// "Nice to see you!" = "Nice To See You!"
|
||||
// "i've read a book! have you?" = "I've Read A Book! Have You?"
|
||||
// "This is `code` ok" = "This Is `code` OK"
|
||||
func Titleize(s string) string {
|
||||
return New(s).Titleize().String()
|
||||
}
|
||||
|
||||
// Titleize will capitalize the start of each part
|
||||
// "Nice to see you!" = "Nice To See You!"
|
||||
// "i've read a book! have you?" = "I've Read A Book! Have You?"
|
||||
// "This is `code` ok" = "This Is `code` OK"
|
||||
func (i Ident) Titleize() Ident {
|
||||
var parts []string
|
||||
|
||||
// TODO: we need to reconsider the design.
|
||||
// this approach preserves inline code block as is but it also
|
||||
// preserves the other words start with a special character.
|
||||
// I would prefer: "*wonderful* world" to be "*Wonderful* World"
|
||||
for _, part := range i.Parts {
|
||||
// CAUTION: in unicode, []rune(str)[0] is not rune(str[0])
|
||||
runes := []rune(part)
|
||||
x := string(unicode.ToTitle(runes[0]))
|
||||
if len(runes) > 1 {
|
||||
x += string(runes[1:])
|
||||
}
|
||||
parts = append(parts, x)
|
||||
}
|
||||
|
||||
return New(strings.Join(parts, " "))
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package flect
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Underscore a string
|
||||
// bob dylan --> bob_dylan
|
||||
// Nice to see you! --> nice_to_see_you
|
||||
// widgetID --> widget_id
|
||||
func Underscore(s string) string {
|
||||
return New(s).Underscore().String()
|
||||
}
|
||||
|
||||
// Underscore a string
|
||||
// bob dylan --> bob_dylan
|
||||
// Nice to see you! --> nice_to_see_you
|
||||
// widgetID --> widget_id
|
||||
func (i Ident) Underscore() Ident {
|
||||
out := make([]string, 0, len(i.Parts))
|
||||
for _, part := range i.Parts {
|
||||
var x strings.Builder
|
||||
x.Grow(len(part))
|
||||
for _, c := range part {
|
||||
if unicode.IsLetter(c) || unicode.IsDigit(c) {
|
||||
x.WriteRune(c)
|
||||
}
|
||||
}
|
||||
if x.Len() > 0 {
|
||||
out = append(out, x.String())
|
||||
}
|
||||
}
|
||||
return New(strings.ToLower(strings.Join(out, "_")))
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package flect
|
||||
|
||||
//Version holds Flect version number
|
||||
const Version = "v1.0.0"
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 apitesting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
|
||||
)
|
||||
|
||||
var (
|
||||
testCodecMediaType string
|
||||
testStorageCodecMediaType string
|
||||
)
|
||||
|
||||
// TestCodec returns the codec for the API version to test against, as set by the
|
||||
// KUBE_TEST_API_TYPE env var.
|
||||
func TestCodec(codecs runtimeserializer.CodecFactory, gvs ...schema.GroupVersion) runtime.Codec {
|
||||
if len(testCodecMediaType) != 0 {
|
||||
serializerInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), testCodecMediaType)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("no serializer for %s", testCodecMediaType))
|
||||
}
|
||||
return codecs.CodecForVersions(serializerInfo.Serializer, codecs.UniversalDeserializer(), schema.GroupVersions(gvs), nil)
|
||||
}
|
||||
return codecs.LegacyCodec(gvs...)
|
||||
}
|
||||
|
||||
// TestStorageCodec returns the codec for the API version to test against used in storage, as set by the
|
||||
// KUBE_TEST_API_STORAGE_TYPE env var.
|
||||
func TestStorageCodec(codecs runtimeserializer.CodecFactory, gvs ...schema.GroupVersion) runtime.Codec {
|
||||
if len(testStorageCodecMediaType) != 0 {
|
||||
serializerInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), testStorageCodecMediaType)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("no serializer for %s", testStorageCodecMediaType))
|
||||
}
|
||||
|
||||
// etcd2 only supports string data - we must wrap any result before returning
|
||||
// TODO: remove for etcd3 / make parameterizable
|
||||
serializer := serializerInfo.Serializer
|
||||
if !serializerInfo.EncodesAsText {
|
||||
serializer = runtime.NewBase64Serializer(serializer, serializer)
|
||||
}
|
||||
|
||||
decoder := recognizer.NewDecoder(serializer, codecs.UniversalDeserializer())
|
||||
return codecs.CodecForVersions(serializer, decoder, schema.GroupVersions(gvs), nil)
|
||||
|
||||
}
|
||||
return codecs.LegacyCodec(gvs...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
|
||||
testCodecMediaType, _, err = mime.ParseMediaType(apiMediaType)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if storageMediaType := os.Getenv("KUBE_TEST_API_STORAGE_TYPE"); len(storageMediaType) > 0 {
|
||||
testStorageCodecMediaType, _, err = mime.ParseMediaType(storageMediaType)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InstallOrDieFunc mirrors install functions that require success
|
||||
type InstallOrDieFunc func(scheme *runtime.Scheme)
|
||||
|
||||
// SchemeForInstallOrDie builds a simple test scheme and codecfactory pair for easy unit testing from higher level install methods
|
||||
func SchemeForInstallOrDie(installFns ...InstallOrDieFunc) (*runtime.Scheme, runtimeserializer.CodecFactory) {
|
||||
scheme := runtime.NewScheme()
|
||||
codecFactory := runtimeserializer.NewCodecFactory(scheme)
|
||||
for _, installFn := range installFns {
|
||||
installFn(scheme)
|
||||
}
|
||||
|
||||
return scheme, codecFactory
|
||||
}
|
||||
|
||||
// InstallFunc mirrors install functions that can return an error
|
||||
type InstallFunc func(scheme *runtime.Scheme) error
|
||||
|
||||
// SchemeForOrDie builds a simple test scheme and codecfactory pair for easy unit testing from the bare registration methods.
|
||||
func SchemeForOrDie(installFns ...InstallFunc) (*runtime.Scheme, runtimeserializer.CodecFactory) {
|
||||
scheme := runtime.NewScheme()
|
||||
codecFactory := runtimeserializer.NewCodecFactory(scheme)
|
||||
for _, installFn := range installFns {
|
||||
if err := installFn(scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return scheme, codecFactory
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 fuzzer
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
)
|
||||
|
||||
// FuzzerFuncs returns a list of func(*SomeType, c fuzz.Continue) functions.
|
||||
type FuzzerFuncs func(codecs runtimeserializer.CodecFactory) []interface{}
|
||||
|
||||
// FuzzerFor can randomly populate api objects that are destined for version.
|
||||
func FuzzerFor(funcs FuzzerFuncs, src rand.Source, codecs runtimeserializer.CodecFactory) *fuzz.Fuzzer {
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 1)
|
||||
if src != nil {
|
||||
f.RandSource(src)
|
||||
}
|
||||
f.Funcs(funcs(codecs)...)
|
||||
return f
|
||||
}
|
||||
|
||||
// MergeFuzzerFuncs will merge the given funcLists, overriding early funcs with later ones if there first
|
||||
// argument has the same type.
|
||||
func MergeFuzzerFuncs(funcs ...FuzzerFuncs) FuzzerFuncs {
|
||||
return FuzzerFuncs(func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
result := []interface{}{}
|
||||
for _, f := range funcs {
|
||||
if f != nil {
|
||||
result = append(result, f(codecs)...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 fuzzer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ValueFuzz recursively changes all basic type values in an object. Any kind of references will not
|
||||
// be touch, i.e. the addresses of slices, maps, pointers will stay unchanged.
|
||||
func ValueFuzz(obj interface{}) {
|
||||
valueFuzz(reflect.ValueOf(obj))
|
||||
}
|
||||
|
||||
func valueFuzz(obj reflect.Value) {
|
||||
switch obj.Kind() {
|
||||
case reflect.Array:
|
||||
for i := 0; i < obj.Len(); i++ {
|
||||
valueFuzz(obj.Index(i))
|
||||
}
|
||||
case reflect.Slice:
|
||||
if obj.IsNil() {
|
||||
// TODO: set non-nil value
|
||||
} else {
|
||||
for i := 0; i < obj.Len(); i++ {
|
||||
valueFuzz(obj.Index(i))
|
||||
}
|
||||
}
|
||||
case reflect.Interface, reflect.Pointer:
|
||||
if obj.IsNil() {
|
||||
// TODO: set non-nil value
|
||||
} else {
|
||||
valueFuzz(obj.Elem())
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i, n := 0, obj.NumField(); i < n; i++ {
|
||||
valueFuzz(obj.Field(i))
|
||||
}
|
||||
case reflect.Map:
|
||||
if obj.IsNil() {
|
||||
// TODO: set non-nil value
|
||||
} else {
|
||||
for _, k := range obj.MapKeys() {
|
||||
// map values are not addressable. We need a copy.
|
||||
v := obj.MapIndex(k)
|
||||
copy := reflect.New(v.Type())
|
||||
copy.Elem().Set(v)
|
||||
valueFuzz(copy.Elem())
|
||||
obj.SetMapIndex(k, copy.Elem())
|
||||
}
|
||||
// TODO: set some new value
|
||||
}
|
||||
case reflect.Func: // ignore, we don't have function types in our API
|
||||
default:
|
||||
if !obj.CanSet() {
|
||||
return
|
||||
}
|
||||
switch obj.Kind() {
|
||||
case reflect.String:
|
||||
obj.SetString(obj.String() + "x")
|
||||
case reflect.Bool:
|
||||
obj.SetBool(!obj.Bool())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
obj.SetFloat(obj.Float()*2.0 + 1.0)
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
obj.SetInt(obj.Int() + 1)
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
obj.SetUint(obj.Uint() + 1)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,337 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 fuzzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
apitesting "k8s.io/apimachinery/pkg/api/apitesting"
|
||||
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(q *resource.Quantity, c fuzz.Continue) {
|
||||
*q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
|
||||
},
|
||||
func(j *int, c fuzz.Continue) {
|
||||
*j = int(c.Int31())
|
||||
},
|
||||
func(j **int, c fuzz.Continue) {
|
||||
if c.RandBool() {
|
||||
i := int(c.Int31())
|
||||
*j = &i
|
||||
} else {
|
||||
*j = nil
|
||||
}
|
||||
},
|
||||
func(j *runtime.TypeMeta, c fuzz.Continue) {
|
||||
// We have to customize the randomization of TypeMetas because their
|
||||
// APIVersion and Kind must remain blank in memory.
|
||||
j.APIVersion = ""
|
||||
j.Kind = ""
|
||||
},
|
||||
func(j *runtime.Object, c fuzz.Continue) {
|
||||
// TODO: uncomment when round trip starts from a versioned object
|
||||
if true { //c.RandBool() {
|
||||
*j = &runtime.Unknown{
|
||||
// We do not set TypeMeta here because it is not carried through a round trip
|
||||
Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
|
||||
ContentType: runtime.ContentTypeJSON,
|
||||
}
|
||||
} else {
|
||||
types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
|
||||
t := types[c.Rand.Intn(len(types))]
|
||||
c.Fuzz(t)
|
||||
*j = t
|
||||
}
|
||||
},
|
||||
func(r *runtime.RawExtension, c fuzz.Continue) {
|
||||
// Pick an arbitrary type and fuzz it
|
||||
types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
|
||||
obj := types[c.Rand.Intn(len(types))]
|
||||
c.Fuzz(obj)
|
||||
|
||||
// Find a codec for converting the object to raw bytes. This is necessary for the
|
||||
// api version and kind to be correctly set be serialization.
|
||||
var codec = apitesting.TestCodec(codecs, metav1.SchemeGroupVersion)
|
||||
|
||||
// Convert the object to raw bytes
|
||||
bytes, err := runtime.Encode(codec, obj)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to encode object: %v", err))
|
||||
}
|
||||
|
||||
// strip trailing newlines which do not survive roundtrips
|
||||
for len(bytes) >= 1 && bytes[len(bytes)-1] == 10 {
|
||||
bytes = bytes[:len(bytes)-1]
|
||||
}
|
||||
|
||||
// Set the bytes field on the RawExtension
|
||||
r.Raw = bytes
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// taken from gofuzz internals for RandString
|
||||
type charRange struct {
|
||||
first, last rune
|
||||
}
|
||||
|
||||
func (c *charRange) choose(r *rand.Rand) rune {
|
||||
count := int64(c.last - c.first + 1)
|
||||
ch := c.first + rune(r.Int63n(count))
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
// randomLabelPart produces a valid random label value or name-part
|
||||
// of a label key.
|
||||
func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string {
|
||||
validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}}
|
||||
validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'},
|
||||
{'.', '.'}, {'-', '-'}, {'_', '_'}}
|
||||
|
||||
partLen := c.Rand.Intn(64) // len is [0, 63]
|
||||
if !canBeEmpty {
|
||||
partLen = c.Rand.Intn(63) + 1 // len is [1, 63]
|
||||
}
|
||||
|
||||
runes := make([]rune, partLen)
|
||||
if partLen == 0 {
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
|
||||
for i := range runes[1:] {
|
||||
runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
|
||||
}
|
||||
runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
|
||||
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func randomDNSLabel(c fuzz.Continue) string {
|
||||
validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}}
|
||||
validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}}
|
||||
|
||||
partLen := c.Rand.Intn(63) + 1 // len is [1, 63]
|
||||
runes := make([]rune, partLen)
|
||||
|
||||
runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
|
||||
for i := range runes[1:] {
|
||||
runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand)
|
||||
}
|
||||
runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand)
|
||||
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func randomLabelKey(c fuzz.Continue) string {
|
||||
namePart := randomLabelPart(c, false)
|
||||
prefixPart := ""
|
||||
|
||||
usePrefix := c.RandBool()
|
||||
if usePrefix {
|
||||
// we can fit, with dots, at most 3 labels in the 253 allotted characters
|
||||
prefixPartsLen := c.Rand.Intn(2) + 1
|
||||
prefixParts := make([]string, prefixPartsLen)
|
||||
for i := range prefixParts {
|
||||
prefixParts[i] = randomDNSLabel(c)
|
||||
}
|
||||
prefixPart = strings.Join(prefixParts, ".") + "/"
|
||||
}
|
||||
|
||||
return prefixPart + namePart
|
||||
}
|
||||
|
||||
func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
|
||||
return []interface{}{
|
||||
func(j *metav1.TypeMeta, c fuzz.Continue) {
|
||||
// We have to customize the randomization of TypeMetas because their
|
||||
// APIVersion and Kind must remain blank in memory.
|
||||
j.APIVersion = ""
|
||||
j.Kind = ""
|
||||
},
|
||||
func(j *metav1.ObjectMeta, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j)
|
||||
|
||||
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
|
||||
j.UID = types.UID(c.RandString())
|
||||
|
||||
// Fuzzing sec and nsec in a smaller range (uint32 instead of int64),
|
||||
// so that the result Unix time is a valid date and can be parsed into RFC3339 format.
|
||||
var sec, nsec uint32
|
||||
c.Fuzz(&sec)
|
||||
c.Fuzz(&nsec)
|
||||
j.CreationTimestamp = metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy()
|
||||
|
||||
if j.DeletionTimestamp != nil {
|
||||
c.Fuzz(&sec)
|
||||
c.Fuzz(&nsec)
|
||||
t := metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy()
|
||||
j.DeletionTimestamp = &t
|
||||
}
|
||||
|
||||
if len(j.Labels) == 0 {
|
||||
j.Labels = nil
|
||||
} else {
|
||||
delete(j.Labels, "")
|
||||
}
|
||||
if len(j.Annotations) == 0 {
|
||||
j.Annotations = nil
|
||||
} else {
|
||||
delete(j.Annotations, "")
|
||||
}
|
||||
if len(j.OwnerReferences) == 0 {
|
||||
j.OwnerReferences = nil
|
||||
}
|
||||
if len(j.Finalizers) == 0 {
|
||||
j.Finalizers = nil
|
||||
}
|
||||
},
|
||||
func(j *metav1.ResourceVersionMatch, c fuzz.Continue) {
|
||||
matches := []metav1.ResourceVersionMatch{"", metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan}
|
||||
*j = matches[c.Rand.Intn(len(matches))]
|
||||
},
|
||||
func(j *metav1.ListMeta, c fuzz.Continue) {
|
||||
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
|
||||
j.SelfLink = c.RandString()
|
||||
},
|
||||
func(j *metav1.LabelSelector, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j)
|
||||
// we can't have an entirely empty selector, so force
|
||||
// use of MatchExpression if necessary
|
||||
if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 {
|
||||
j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1)
|
||||
}
|
||||
|
||||
if j.MatchLabels != nil {
|
||||
fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels))
|
||||
for i := 0; i < len(j.MatchLabels); i++ {
|
||||
fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true)
|
||||
}
|
||||
j.MatchLabels = fuzzedMatchLabels
|
||||
}
|
||||
|
||||
validOperators := []metav1.LabelSelectorOperator{
|
||||
metav1.LabelSelectorOpIn,
|
||||
metav1.LabelSelectorOpNotIn,
|
||||
metav1.LabelSelectorOpExists,
|
||||
metav1.LabelSelectorOpDoesNotExist,
|
||||
}
|
||||
|
||||
if j.MatchExpressions != nil {
|
||||
// NB: the label selector parser code sorts match expressions by key, and sorts the values,
|
||||
// so we need to make sure ours are sorted as well here to preserve round-trip comparison.
|
||||
// In practice, not sorting doesn't hurt anything...
|
||||
|
||||
for i := range j.MatchExpressions {
|
||||
req := metav1.LabelSelectorRequirement{}
|
||||
c.Fuzz(&req)
|
||||
req.Key = randomLabelKey(c)
|
||||
req.Operator = validOperators[c.Rand.Intn(len(validOperators))]
|
||||
if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn {
|
||||
if len(req.Values) == 0 {
|
||||
// we must have some values here, so randomly choose a short length
|
||||
req.Values = make([]string, c.Rand.Intn(2)+1)
|
||||
}
|
||||
for i := range req.Values {
|
||||
req.Values[i] = randomLabelPart(c, true)
|
||||
}
|
||||
sort.Strings(req.Values)
|
||||
} else {
|
||||
req.Values = nil
|
||||
}
|
||||
j.MatchExpressions[i] = req
|
||||
}
|
||||
|
||||
sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key })
|
||||
}
|
||||
},
|
||||
func(j *metav1.ManagedFieldsEntry, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(j)
|
||||
j.FieldsV1 = nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func v1beta1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(r *metav1beta1.TableOptions, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(r)
|
||||
// NoHeaders is not serialized to the wire but is allowed within the versioned
|
||||
// type because we don't use meta internal types in the client and API server.
|
||||
r.NoHeaders = false
|
||||
},
|
||||
func(r *metav1beta1.TableRow, c fuzz.Continue) {
|
||||
c.Fuzz(&r.Object)
|
||||
c.Fuzz(&r.Conditions)
|
||||
if len(r.Conditions) == 0 {
|
||||
r.Conditions = nil
|
||||
}
|
||||
n := c.Intn(10)
|
||||
if n > 0 {
|
||||
r.Cells = make([]interface{}, n)
|
||||
}
|
||||
for i := range r.Cells {
|
||||
t := c.Intn(6)
|
||||
switch t {
|
||||
case 0:
|
||||
r.Cells[i] = c.RandString()
|
||||
case 1:
|
||||
r.Cells[i] = c.Int63()
|
||||
case 2:
|
||||
r.Cells[i] = c.RandBool()
|
||||
case 3:
|
||||
x := map[string]interface{}{}
|
||||
for j := c.Intn(10) + 1; j >= 0; j-- {
|
||||
x[c.RandString()] = c.RandString()
|
||||
}
|
||||
r.Cells[i] = x
|
||||
case 4:
|
||||
x := make([]interface{}, c.Intn(10))
|
||||
for i := range x {
|
||||
x[i] = c.Int63()
|
||||
}
|
||||
r.Cells[i] = x
|
||||
default:
|
||||
r.Cells[i] = nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var Funcs = fuzzer.MergeFuzzerFuncs(
|
||||
genericFuzzerFuncs,
|
||||
v1FuzzerFuncs,
|
||||
v1beta1FuzzerFuncs,
|
||||
)
|
|
@ -113,9 +113,6 @@ github.com/go-openapi/swag
|
|||
# github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
|
||||
## explicit; go 1.13
|
||||
github.com/go-task/slim-sprig
|
||||
# github.com/gobuffalo/flect v1.0.2
|
||||
## explicit; go 1.16
|
||||
github.com/gobuffalo/flect
|
||||
# github.com/gogo/protobuf v1.3.2
|
||||
## explicit; go 1.15
|
||||
github.com/gogo/protobuf/gogoproto
|
||||
|
@ -876,8 +873,6 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextension
|
|||
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1
|
||||
# k8s.io/apimachinery v0.26.2
|
||||
## explicit; go 1.19
|
||||
k8s.io/apimachinery/pkg/api/apitesting
|
||||
k8s.io/apimachinery/pkg/api/apitesting/fuzzer
|
||||
k8s.io/apimachinery/pkg/api/equality
|
||||
k8s.io/apimachinery/pkg/api/errors
|
||||
k8s.io/apimachinery/pkg/api/meta
|
||||
|
@ -886,7 +881,6 @@ k8s.io/apimachinery/pkg/api/meta/testrestmapper
|
|||
k8s.io/apimachinery/pkg/api/resource
|
||||
k8s.io/apimachinery/pkg/api/validation
|
||||
k8s.io/apimachinery/pkg/api/validation/path
|
||||
k8s.io/apimachinery/pkg/apis/meta/fuzzer
|
||||
k8s.io/apimachinery/pkg/apis/meta/internalversion
|
||||
k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme
|
||||
k8s.io/apimachinery/pkg/apis/meta/internalversion/validation
|
||||
|
@ -1648,17 +1642,12 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics
|
|||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client
|
||||
# sigs.k8s.io/cluster-api v1.4.0
|
||||
## explicit; go 1.19
|
||||
sigs.k8s.io/cluster-api/api/v1alpha4
|
||||
sigs.k8s.io/cluster-api/api/v1beta1
|
||||
sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1
|
||||
sigs.k8s.io/cluster-api/errors
|
||||
sigs.k8s.io/cluster-api/feature
|
||||
sigs.k8s.io/cluster-api/internal/labels
|
||||
sigs.k8s.io/cluster-api/util
|
||||
sigs.k8s.io/cluster-api/util/annotations
|
||||
sigs.k8s.io/cluster-api/util/certs
|
||||
sigs.k8s.io/cluster-api/util/contract
|
||||
sigs.k8s.io/cluster-api/util/conversion
|
||||
sigs.k8s.io/cluster-api/util/secret
|
||||
sigs.k8s.io/cluster-api/util/version
|
||||
# sigs.k8s.io/controller-runtime v0.14.5
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
// ClusterPhase is a string representation of a Cluster Phase.
|
||||
//
|
||||
// This type is a high-level indicator of the status of the Cluster as it is provisioned,
|
||||
// from the API user’s perspective.
|
||||
//
|
||||
// The value should not be interpreted by any software components as a reliable indication
|
||||
// of the actual state of the Cluster, and controllers should not use the Cluster Phase field
|
||||
// value when making decisions about what action to take.
|
||||
//
|
||||
// Controllers should always look at the actual state of the Cluster’s fields to make those decisions.
|
||||
type ClusterPhase string
|
||||
|
||||
const (
|
||||
// ClusterPhasePending is the first state a Cluster is assigned by
|
||||
// Cluster API Cluster controller after being created.
|
||||
ClusterPhasePending = ClusterPhase("Pending")
|
||||
|
||||
// ClusterPhaseProvisioning is the state when the Cluster has a provider infrastructure
|
||||
// object associated and can start provisioning.
|
||||
ClusterPhaseProvisioning = ClusterPhase("Provisioning")
|
||||
|
||||
// ClusterPhaseProvisioned is the state when its
|
||||
// infrastructure has been created and configured.
|
||||
ClusterPhaseProvisioned = ClusterPhase("Provisioned")
|
||||
|
||||
// ClusterPhaseDeleting is the Cluster state when a delete
|
||||
// request has been sent to the API Server,
|
||||
// but its infrastructure has not yet been fully deleted.
|
||||
ClusterPhaseDeleting = ClusterPhase("Deleting")
|
||||
|
||||
// ClusterPhaseFailed is the Cluster state when the system
|
||||
// might require user intervention.
|
||||
ClusterPhaseFailed = ClusterPhase("Failed")
|
||||
|
||||
// ClusterPhaseUnknown is returned if the Cluster state cannot be determined.
|
||||
ClusterPhaseUnknown = ClusterPhase("Unknown")
|
||||
)
|
|
@ -1,438 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
capierrors "sigs.k8s.io/cluster-api/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// ClusterFinalizer is the finalizer used by the cluster controller to
|
||||
// cleanup the cluster resources when a Cluster is being deleted.
|
||||
ClusterFinalizer = "cluster.cluster.x-k8s.io"
|
||||
)
|
||||
|
||||
// ANCHOR: ClusterSpec
|
||||
|
||||
// ClusterSpec defines the desired state of Cluster.
|
||||
type ClusterSpec struct {
|
||||
// Paused can be used to prevent controllers from processing the Cluster and all its associated objects.
|
||||
// +optional
|
||||
Paused bool `json:"paused,omitempty"`
|
||||
|
||||
// Cluster network configuration.
|
||||
// +optional
|
||||
ClusterNetwork *ClusterNetwork `json:"clusterNetwork,omitempty"`
|
||||
|
||||
// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
|
||||
// +optional
|
||||
ControlPlaneEndpoint APIEndpoint `json:"controlPlaneEndpoint"`
|
||||
|
||||
// ControlPlaneRef is an optional reference to a provider-specific resource that holds
|
||||
// the details for provisioning the Control Plane for a Cluster.
|
||||
// +optional
|
||||
ControlPlaneRef *corev1.ObjectReference `json:"controlPlaneRef,omitempty"`
|
||||
|
||||
// InfrastructureRef is a reference to a provider-specific resource that holds the details
|
||||
// for provisioning infrastructure for a cluster in said provider.
|
||||
// +optional
|
||||
InfrastructureRef *corev1.ObjectReference `json:"infrastructureRef,omitempty"`
|
||||
|
||||
// This encapsulates the topology for the cluster.
|
||||
// NOTE: It is required to enable the ClusterTopology
|
||||
// feature gate flag to activate managed topologies support;
|
||||
// this feature is highly experimental, and parts of it might still be not implemented.
|
||||
// +optional
|
||||
Topology *Topology `json:"topology,omitempty"`
|
||||
}
|
||||
|
||||
// Topology encapsulates the information of the managed resources.
|
||||
type Topology struct {
|
||||
// The name of the ClusterClass object to create the topology.
|
||||
Class string `json:"class"`
|
||||
|
||||
// The Kubernetes version of the cluster.
|
||||
Version string `json:"version"`
|
||||
|
||||
// RolloutAfter performs a rollout of the entire cluster one component at a time,
|
||||
// control plane first and then machine deployments.
|
||||
// +optional
|
||||
RolloutAfter *metav1.Time `json:"rolloutAfter,omitempty"`
|
||||
|
||||
// ControlPlane describes the cluster control plane.
|
||||
// +optional
|
||||
ControlPlane ControlPlaneTopology `json:"controlPlane"`
|
||||
|
||||
// Workers encapsulates the different constructs that form the worker nodes
|
||||
// for the cluster.
|
||||
// +optional
|
||||
Workers *WorkersTopology `json:"workers,omitempty"`
|
||||
}
|
||||
|
||||
// ControlPlaneTopology specifies the parameters for the control plane nodes in the cluster.
|
||||
type ControlPlaneTopology struct {
|
||||
// Metadata is the metadata applied to the machines of the ControlPlane.
|
||||
// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
|
||||
//
|
||||
// This field is supported if and only if the control plane provider template
|
||||
// referenced in the ClusterClass is Machine based.
|
||||
Metadata ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Replicas is the number of control plane nodes.
|
||||
// If the value is nil, the ControlPlane object is created without the number of Replicas
|
||||
// and it's assumed that the control plane controller does not implement support for this field.
|
||||
// When specified against a control plane provider that lacks support for this field, this value will be ignored.
|
||||
// +optional
|
||||
Replicas *int32 `json:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
// WorkersTopology represents the different sets of worker nodes in the cluster.
|
||||
type WorkersTopology struct {
|
||||
// MachineDeployments is a list of machine deployments in the cluster.
|
||||
MachineDeployments []MachineDeploymentTopology `json:"machineDeployments,omitempty"`
|
||||
}
|
||||
|
||||
// MachineDeploymentTopology specifies the different parameters for a set of worker nodes in the topology.
|
||||
// This set of nodes is managed by a MachineDeployment object whose lifecycle is managed by the Cluster controller.
|
||||
type MachineDeploymentTopology struct {
|
||||
// Metadata is the metadata applied to the machines of the MachineDeployment.
|
||||
// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
|
||||
Metadata ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Class is the name of the MachineDeploymentClass used to create the set of worker nodes.
|
||||
// This should match one of the deployment classes defined in the ClusterClass object
|
||||
// mentioned in the `Cluster.Spec.Class` field.
|
||||
Class string `json:"class"`
|
||||
|
||||
// Name is the unique identifier for this MachineDeploymentTopology.
|
||||
// The value is used with other unique identifiers to create a MachineDeployment's Name
|
||||
// (e.g. cluster's name, etc). In case the name is greater than the allowed maximum length,
|
||||
// the values are hashed together.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Replicas is the number of worker nodes belonging to this set.
|
||||
// If the value is nil, the MachineDeployment is created without the number of Replicas (defaulting to zero)
|
||||
// and it's assumed that an external entity (like cluster autoscaler) is responsible for the management
|
||||
// of this value.
|
||||
// +optional
|
||||
Replicas *int32 `json:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: ClusterSpec
|
||||
|
||||
// ANCHOR: ClusterNetwork
|
||||
|
||||
// ClusterNetwork specifies the different networking
|
||||
// parameters for a cluster.
|
||||
type ClusterNetwork struct {
|
||||
// APIServerPort specifies the port the API Server should bind to.
|
||||
// Defaults to 6443.
|
||||
// +optional
|
||||
APIServerPort *int32 `json:"apiServerPort,omitempty"`
|
||||
|
||||
// The network ranges from which service VIPs are allocated.
|
||||
// +optional
|
||||
Services *NetworkRanges `json:"services,omitempty"`
|
||||
|
||||
// The network ranges from which Pod networks are allocated.
|
||||
// +optional
|
||||
Pods *NetworkRanges `json:"pods,omitempty"`
|
||||
|
||||
// Domain name for services.
|
||||
// +optional
|
||||
ServiceDomain string `json:"serviceDomain,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: ClusterNetwork
|
||||
|
||||
// ANCHOR: NetworkRanges
|
||||
|
||||
// NetworkRanges represents ranges of network addresses.
|
||||
type NetworkRanges struct {
|
||||
CIDRBlocks []string `json:"cidrBlocks"`
|
||||
}
|
||||
|
||||
func (n *NetworkRanges) String() string {
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(n.CIDRBlocks, ",")
|
||||
}
|
||||
|
||||
// ANCHOR_END: NetworkRanges
|
||||
|
||||
// ANCHOR: ClusterStatus
|
||||
|
||||
// ClusterStatus defines the observed state of Cluster.
|
||||
type ClusterStatus struct {
|
||||
// FailureDomains is a slice of failure domain objects synced from the infrastructure provider.
|
||||
FailureDomains FailureDomains `json:"failureDomains,omitempty"`
|
||||
|
||||
// FailureReason indicates that there is a fatal problem reconciling the
|
||||
// state, and will be set to a token value suitable for
|
||||
// programmatic interpretation.
|
||||
// +optional
|
||||
FailureReason *capierrors.ClusterStatusError `json:"failureReason,omitempty"`
|
||||
|
||||
// FailureMessage indicates that there is a fatal problem reconciling the
|
||||
// state, and will be set to a descriptive error message.
|
||||
// +optional
|
||||
FailureMessage *string `json:"failureMessage,omitempty"`
|
||||
|
||||
// Phase represents the current phase of cluster actuation.
|
||||
// E.g. Pending, Running, Terminating, Failed etc.
|
||||
// +optional
|
||||
Phase string `json:"phase,omitempty"`
|
||||
|
||||
// InfrastructureReady is the state of the infrastructure provider.
|
||||
// +optional
|
||||
InfrastructureReady bool `json:"infrastructureReady"`
|
||||
|
||||
// ControlPlaneReady defines if the control plane is ready.
|
||||
// +optional
|
||||
ControlPlaneReady bool `json:"controlPlaneReady,omitempty"`
|
||||
|
||||
// Conditions defines current service state of the cluster.
|
||||
// +optional
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
|
||||
// ObservedGeneration is the latest generation observed by the controller.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: ClusterStatus
|
||||
|
||||
// SetTypedPhase sets the Phase field to the string representation of ClusterPhase.
|
||||
func (c *ClusterStatus) SetTypedPhase(p ClusterPhase) {
|
||||
c.Phase = string(p)
|
||||
}
|
||||
|
||||
// GetTypedPhase attempts to parse the Phase field and return
|
||||
// the typed ClusterPhase representation as described in `machine_phase_types.go`.
|
||||
func (c *ClusterStatus) GetTypedPhase() ClusterPhase {
|
||||
switch phase := ClusterPhase(c.Phase); phase {
|
||||
case
|
||||
ClusterPhasePending,
|
||||
ClusterPhaseProvisioning,
|
||||
ClusterPhaseProvisioned,
|
||||
ClusterPhaseDeleting,
|
||||
ClusterPhaseFailed:
|
||||
return phase
|
||||
default:
|
||||
return ClusterPhaseUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: APIEndpoint
|
||||
|
||||
// APIEndpoint represents a reachable Kubernetes API endpoint.
|
||||
type APIEndpoint struct {
|
||||
// The hostname on which the API server is serving.
|
||||
Host string `json:"host"`
|
||||
|
||||
// The port on which the API server is serving.
|
||||
Port int32 `json:"port"`
|
||||
}
|
||||
|
||||
// IsZero returns true if both host and port are zero values.
|
||||
func (v APIEndpoint) IsZero() bool {
|
||||
return v.Host == "" && v.Port == 0
|
||||
}
|
||||
|
||||
// IsValid returns true if both host and port are non-zero values.
|
||||
func (v APIEndpoint) IsValid() bool {
|
||||
return v.Host != "" && v.Port != 0
|
||||
}
|
||||
|
||||
// String returns a formatted version HOST:PORT of this APIEndpoint.
|
||||
func (v APIEndpoint) String() string {
|
||||
return net.JoinHostPort(v.Host, fmt.Sprintf("%d", v.Port))
|
||||
}
|
||||
|
||||
// ANCHOR_END: APIEndpoint
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=clusters,shortName=cl,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Cluster"
|
||||
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Cluster status such as Pending/Provisioning/Provisioned/Deleting/Failed"
|
||||
|
||||
// Cluster is the Schema for the clusters API.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type Cluster struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ClusterSpec `json:"spec,omitempty"`
|
||||
Status ClusterStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GetConditions returns the set of conditions for this object.
|
||||
func (c *Cluster) GetConditions() Conditions {
|
||||
return c.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions sets the conditions on this object.
|
||||
func (c *Cluster) SetConditions(conditions Conditions) {
|
||||
c.Status.Conditions = conditions
|
||||
}
|
||||
|
||||
// GetIPFamily returns a ClusterIPFamily from the configuration provided.
|
||||
func (c *Cluster) GetIPFamily() (ClusterIPFamily, error) {
|
||||
var podCIDRs, serviceCIDRs []string
|
||||
if c.Spec.ClusterNetwork != nil {
|
||||
if c.Spec.ClusterNetwork.Pods != nil {
|
||||
podCIDRs = c.Spec.ClusterNetwork.Pods.CIDRBlocks
|
||||
}
|
||||
if c.Spec.ClusterNetwork.Services != nil {
|
||||
serviceCIDRs = c.Spec.ClusterNetwork.Services.CIDRBlocks
|
||||
}
|
||||
}
|
||||
if len(podCIDRs) == 0 && len(serviceCIDRs) == 0 {
|
||||
return IPv4IPFamily, nil
|
||||
}
|
||||
|
||||
podsIPFamily, err := ipFamilyForCIDRStrings(podCIDRs)
|
||||
if err != nil {
|
||||
return InvalidIPFamily, fmt.Errorf("pods: %s", err)
|
||||
}
|
||||
if len(serviceCIDRs) == 0 {
|
||||
return podsIPFamily, nil
|
||||
}
|
||||
|
||||
servicesIPFamily, err := ipFamilyForCIDRStrings(serviceCIDRs)
|
||||
if err != nil {
|
||||
return InvalidIPFamily, fmt.Errorf("services: %s", err)
|
||||
}
|
||||
if len(podCIDRs) == 0 {
|
||||
return servicesIPFamily, nil
|
||||
}
|
||||
|
||||
if podsIPFamily == DualStackIPFamily {
|
||||
return DualStackIPFamily, nil
|
||||
} else if podsIPFamily != servicesIPFamily {
|
||||
return InvalidIPFamily, errors.New("pods and services IP family mismatch")
|
||||
}
|
||||
|
||||
return podsIPFamily, nil
|
||||
}
|
||||
|
||||
func ipFamilyForCIDRStrings(cidrs []string) (ClusterIPFamily, error) {
|
||||
if len(cidrs) > 2 {
|
||||
return InvalidIPFamily, errors.New("too many CIDRs specified")
|
||||
}
|
||||
var foundIPv4 bool
|
||||
var foundIPv6 bool
|
||||
for _, cidr := range cidrs {
|
||||
ip, _, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return InvalidIPFamily, fmt.Errorf("could not parse CIDR: %s", err)
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
foundIPv4 = true
|
||||
} else {
|
||||
foundIPv6 = true
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case foundIPv4 && foundIPv6:
|
||||
return DualStackIPFamily, nil
|
||||
case foundIPv4:
|
||||
return IPv4IPFamily, nil
|
||||
case foundIPv6:
|
||||
return IPv6IPFamily, nil
|
||||
default:
|
||||
return InvalidIPFamily, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterIPFamily defines the types of supported IP families.
|
||||
type ClusterIPFamily int
|
||||
|
||||
// Define the ClusterIPFamily constants.
|
||||
const (
|
||||
InvalidIPFamily ClusterIPFamily = iota
|
||||
IPv4IPFamily
|
||||
IPv6IPFamily
|
||||
DualStackIPFamily
|
||||
)
|
||||
|
||||
func (f ClusterIPFamily) String() string {
|
||||
return [...]string{"InvalidIPFamily", "IPv4IPFamily", "IPv6IPFamily", "DualStackIPFamily"}[f]
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ClusterList contains a list of Cluster.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type ClusterList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Cluster `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Cluster{}, &ClusterList{})
|
||||
}
|
||||
|
||||
// FailureDomains is a slice of FailureDomains.
|
||||
type FailureDomains map[string]FailureDomainSpec
|
||||
|
||||
// FilterControlPlane returns a FailureDomain slice containing only the domains suitable to be used
|
||||
// for control plane nodes.
|
||||
func (in FailureDomains) FilterControlPlane() FailureDomains {
|
||||
res := make(FailureDomains)
|
||||
for id, spec := range in {
|
||||
if spec.ControlPlane {
|
||||
res[id] = spec
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetIDs returns a slice containing the ids for failure domains.
|
||||
func (in FailureDomains) GetIDs() []*string {
|
||||
ids := make([]*string, 0, len(in))
|
||||
for id := range in {
|
||||
ids = append(ids, pointer.String(id))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// FailureDomainSpec is the Schema for Cluster API failure domains.
|
||||
// It allows controllers to understand how many failure domains a cluster can optionally span across.
|
||||
type FailureDomainSpec struct {
|
||||
// ControlPlane determines if this failure domain is suitable for use by control plane machines.
|
||||
// +optional
|
||||
ControlPlane bool `json:"controlPlane"`
|
||||
|
||||
// Attributes is a free form map of attributes an infrastructure provider might use or require.
|
||||
// +optional
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 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 v1alpha4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=clusterclasses,shortName=cc,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of ClusterClass"
|
||||
|
||||
// ClusterClass is a template which can be used to create managed topologies.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type ClusterClass struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ClusterClassSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// ClusterClassSpec describes the desired state of the ClusterClass.
|
||||
type ClusterClassSpec struct {
|
||||
// Infrastructure is a reference to a provider-specific template that holds
|
||||
// the details for provisioning infrastructure specific cluster
|
||||
// for the underlying provider.
|
||||
// The underlying provider is responsible for the implementation
|
||||
// of the template to an infrastructure cluster.
|
||||
Infrastructure LocalObjectTemplate `json:"infrastructure,omitempty"`
|
||||
|
||||
// ControlPlane is a reference to a local struct that holds the details
|
||||
// for provisioning the Control Plane for the Cluster.
|
||||
ControlPlane ControlPlaneClass `json:"controlPlane,omitempty"`
|
||||
|
||||
// Workers describes the worker nodes for the cluster.
|
||||
// It is a collection of node types which can be used to create
|
||||
// the worker nodes of the cluster.
|
||||
// +optional
|
||||
Workers WorkersClass `json:"workers,omitempty"`
|
||||
}
|
||||
|
||||
// ControlPlaneClass defines the class for the control plane.
|
||||
type ControlPlaneClass struct {
|
||||
// Metadata is the metadata applied to the machines of the ControlPlane.
|
||||
// At runtime this metadata is merged with the corresponding metadata from the topology.
|
||||
//
|
||||
// This field is supported if and only if the control plane provider template
|
||||
// referenced is Machine based.
|
||||
Metadata ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// LocalObjectTemplate contains the reference to the control plane provider.
|
||||
LocalObjectTemplate `json:",inline"`
|
||||
|
||||
// MachineTemplate defines the metadata and infrastructure information
|
||||
// for control plane machines.
|
||||
//
|
||||
// This field is supported if and only if the control plane provider template
|
||||
// referenced above is Machine based and supports setting replicas.
|
||||
//
|
||||
// +optional
|
||||
MachineInfrastructure *LocalObjectTemplate `json:"machineInfrastructure,omitempty"`
|
||||
}
|
||||
|
||||
// WorkersClass is a collection of deployment classes.
|
||||
type WorkersClass struct {
|
||||
// MachineDeployments is a list of machine deployment classes that can be used to create
|
||||
// a set of worker nodes.
|
||||
MachineDeployments []MachineDeploymentClass `json:"machineDeployments,omitempty"`
|
||||
}
|
||||
|
||||
// MachineDeploymentClass serves as a template to define a set of worker nodes of the cluster
|
||||
// provisioned using the `ClusterClass`.
|
||||
type MachineDeploymentClass struct {
|
||||
// Class denotes a type of worker node present in the cluster,
|
||||
// this name MUST be unique within a ClusterClass and can be referenced
|
||||
// in the Cluster to create a managed MachineDeployment.
|
||||
Class string `json:"class"`
|
||||
|
||||
// Template is a local struct containing a collection of templates for creation of
|
||||
// MachineDeployment objects representing a set of worker nodes.
|
||||
Template MachineDeploymentClassTemplate `json:"template"`
|
||||
}
|
||||
|
||||
// MachineDeploymentClassTemplate defines how a MachineDeployment generated from a MachineDeploymentClass
|
||||
// should look like.
|
||||
type MachineDeploymentClassTemplate struct {
|
||||
// Metadata is the metadata applied to the machines of the MachineDeployment.
|
||||
// At runtime this metadata is merged with the corresponding metadata from the topology.
|
||||
Metadata ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Bootstrap contains the bootstrap template reference to be used
|
||||
// for the creation of worker Machines.
|
||||
Bootstrap LocalObjectTemplate `json:"bootstrap"`
|
||||
|
||||
// Infrastructure contains the infrastructure template reference to be used
|
||||
// for the creation of worker Machines.
|
||||
Infrastructure LocalObjectTemplate `json:"infrastructure"`
|
||||
}
|
||||
|
||||
// LocalObjectTemplate defines a template for a topology Class.
|
||||
type LocalObjectTemplate struct {
|
||||
// Ref is a required reference to a custom resource
|
||||
// offered by a provider.
|
||||
Ref *corev1.ObjectReference `json:"ref"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ClusterClassList contains a list of Cluster.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type ClusterClassList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ClusterClass `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&ClusterClass{}, &ClusterClassList{})
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// ClusterLabelName is the label set on machines linked to a cluster and
|
||||
// external objects(bootstrap and infrastructure providers).
|
||||
ClusterLabelName = "cluster.x-k8s.io/cluster-name"
|
||||
|
||||
// ClusterTopologyLabelName is the label set on all the object which are managed as part of a ClusterTopology.
|
||||
//
|
||||
// Deprecated: use ClusterTopologyOwnedLabel instead.
|
||||
ClusterTopologyLabelName = "cluster.x-k8s.io/topology"
|
||||
|
||||
// ClusterTopologyOwnedLabel is the label set on all the object which are managed as part of a ClusterTopology.
|
||||
ClusterTopologyOwnedLabel = "topology.cluster.x-k8s.io/owned"
|
||||
|
||||
// ClusterTopologyMachineDeploymentLabelName is the label set on the generated MachineDeployment objects
|
||||
// to track the name of the MachineDeployment topology it represents.
|
||||
ClusterTopologyMachineDeploymentLabelName = "topology.cluster.x-k8s.io/deployment-name"
|
||||
|
||||
// ProviderLabelName is the label set on components in the provider manifest.
|
||||
// This label allows to easily identify all the components belonging to a provider; the clusterctl
|
||||
// tool uses this label for implementing provider's lifecycle operations.
|
||||
ProviderLabelName = "cluster.x-k8s.io/provider"
|
||||
|
||||
// ClusterNameAnnotation is the annotation set on nodes identifying the name of the cluster the node belongs to.
|
||||
ClusterNameAnnotation = "cluster.x-k8s.io/cluster-name"
|
||||
|
||||
// ClusterNamespaceAnnotation is the annotation set on nodes identifying the namespace of the cluster the node belongs to.
|
||||
ClusterNamespaceAnnotation = "cluster.x-k8s.io/cluster-namespace"
|
||||
|
||||
// MachineAnnotation is the annotation set on nodes identifying the machine the node belongs to.
|
||||
MachineAnnotation = "cluster.x-k8s.io/machine"
|
||||
|
||||
// OwnerKindAnnotation is the annotation set on nodes identifying the owner kind.
|
||||
OwnerKindAnnotation = "cluster.x-k8s.io/owner-kind"
|
||||
|
||||
// OwnerNameAnnotation is the annotation set on nodes identifying the owner name.
|
||||
OwnerNameAnnotation = "cluster.x-k8s.io/owner-name"
|
||||
|
||||
// PausedAnnotation is an annotation that can be applied to any Cluster API
|
||||
// object to prevent a controller from processing a resource.
|
||||
//
|
||||
// Controllers working with Cluster API objects must check the existence of this annotation
|
||||
// on the reconciled object.
|
||||
PausedAnnotation = "cluster.x-k8s.io/paused"
|
||||
|
||||
// DisableMachineCreate is an annotation that can be used to signal a MachineSet to stop creating new machines.
|
||||
// It is utilized in the OnDelete MachineDeploymentStrategy to allow the MachineDeployment controller to scale down
|
||||
// older MachineSets when Machines are deleted and add the new replicas to the latest MachineSet.
|
||||
DisableMachineCreate = "cluster.x-k8s.io/disable-machine-create"
|
||||
|
||||
// WatchLabel is a label othat can be applied to any Cluster API object.
|
||||
//
|
||||
// Controllers which allow for selective reconciliation may check this label and proceed
|
||||
// with reconciliation of the object only if this label and a configured value is present.
|
||||
WatchLabel = "cluster.x-k8s.io/watch-filter"
|
||||
|
||||
// DeleteMachineAnnotation marks control plane and worker nodes that will be given priority for deletion
|
||||
// when KCP or a machineset scales down. This annotation is given top priority on all delete policies.
|
||||
DeleteMachineAnnotation = "cluster.x-k8s.io/delete-machine"
|
||||
|
||||
// TemplateClonedFromNameAnnotation is the infrastructure machine annotation that stores the name of the infrastructure template resource
|
||||
// that was cloned for the machine. This annotation is set only during cloning a template. Older/adopted machines will not have this annotation.
|
||||
TemplateClonedFromNameAnnotation = "cluster.x-k8s.io/cloned-from-name"
|
||||
|
||||
// TemplateClonedFromGroupKindAnnotation is the infrastructure machine annotation that stores the group-kind of the infrastructure template resource
|
||||
// that was cloned for the machine. This annotation is set only during cloning a template. Older/adopted machines will not have this annotation.
|
||||
TemplateClonedFromGroupKindAnnotation = "cluster.x-k8s.io/cloned-from-groupkind"
|
||||
|
||||
// MachineSkipRemediationAnnotation is the annotation used to mark the machines that should not be considered for remediation by MachineHealthCheck reconciler.
|
||||
MachineSkipRemediationAnnotation = "cluster.x-k8s.io/skip-remediation"
|
||||
|
||||
// ClusterSecretType defines the type of secret created by core components.
|
||||
ClusterSecretType corev1.SecretType = "cluster.x-k8s.io/secret" //nolint:gosec
|
||||
|
||||
// InterruptibleLabel is the label used to mark the nodes that run on interruptible instances.
|
||||
InterruptibleLabel = "cluster.x-k8s.io/interruptible"
|
||||
|
||||
// ManagedByAnnotation is an annotation that can be applied to InfraCluster resources to signify that
|
||||
// some external system is managing the cluster infrastructure.
|
||||
//
|
||||
// Provider InfraCluster controllers will ignore resources with this annotation.
|
||||
// An external controller must fulfill the contract of the InfraCluster resource.
|
||||
// External infrastructure providers should ensure that the annotation, once set, cannot be removed.
|
||||
ManagedByAnnotation = "cluster.x-k8s.io/managed-by"
|
||||
)
|
||||
|
||||
const (
|
||||
// TemplateSuffix is the object kind suffix used by template types.
|
||||
TemplateSuffix = "Template"
|
||||
)
|
||||
|
||||
var (
|
||||
// ZeroDuration is a zero value of the metav1.Duration type.
|
||||
ZeroDuration = metav1.Duration{}
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineNodeNameIndex is used by the Machine Controller to index Machines by Node name, and add a watch on Nodes.
|
||||
//
|
||||
// Deprecated: Use api/v1alpha4/index.MachineNodeNameField instead.
|
||||
MachineNodeNameIndex = "status.nodeRef.name"
|
||||
|
||||
// MachineProviderIDIndex is used to index Machines by ProviderID. It's useful to find Machines
|
||||
// in a management cluster from Nodes in a workload cluster.
|
||||
//
|
||||
// Deprecated: Use api/v1alpha4/index.MachineProviderIDField instead.
|
||||
MachineProviderIDIndex = "spec.providerID"
|
||||
)
|
||||
|
||||
// MachineAddressType describes a valid MachineAddress type.
|
||||
type MachineAddressType string
|
||||
|
||||
// Define the MachineAddressType constants.
|
||||
const (
|
||||
MachineHostName MachineAddressType = "Hostname"
|
||||
MachineExternalIP MachineAddressType = "ExternalIP"
|
||||
MachineInternalIP MachineAddressType = "InternalIP"
|
||||
MachineExternalDNS MachineAddressType = "ExternalDNS"
|
||||
MachineInternalDNS MachineAddressType = "InternalDNS"
|
||||
)
|
||||
|
||||
// MachineAddress contains information for the node's address.
|
||||
type MachineAddress struct {
|
||||
// Machine address type, one of Hostname, ExternalIP or InternalIP.
|
||||
Type MachineAddressType `json:"type"`
|
||||
|
||||
// The machine address.
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// MachineAddresses is a slice of MachineAddress items to be used by infrastructure providers.
|
||||
type MachineAddresses []MachineAddress
|
||||
|
||||
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
|
||||
// users must create. This is a copy of customizable fields from metav1.ObjectMeta.
|
||||
//
|
||||
// ObjectMeta is embedded in `Machine.Spec`, `MachineDeployment.Template` and `MachineSet.Template`,
|
||||
// which are not top-level Kubernetes objects. Given that metav1.ObjectMeta has lots of special cases
|
||||
// and read-only fields which end up in the generated CRD validation, having it as a subset simplifies
|
||||
// the API and some issues that can impact user experience.
|
||||
//
|
||||
// During the [upgrade to controller-tools@v2](https://github.com/kubernetes-sigs/cluster-api/pull/1054)
|
||||
// for v1alpha2, we noticed a failure would occur running Cluster API test suite against the new CRDs,
|
||||
// specifically `spec.metadata.creationTimestamp in body must be of type string: "null"`.
|
||||
// The investigation showed that `controller-tools@v2` behaves differently than its previous version
|
||||
// when handling types from [metav1](k8s.io/apimachinery/pkg/apis/meta/v1) package.
|
||||
//
|
||||
// In more details, we found that embedded (non-top level) types that embedded `metav1.ObjectMeta`
|
||||
// had validation properties, including for `creationTimestamp` (metav1.Time).
|
||||
// The `metav1.Time` type specifies a custom json marshaller that, when IsZero() is true, returns `null`
|
||||
// which breaks validation because the field isn't marked as nullable.
|
||||
//
|
||||
// In future versions, controller-tools@v2 might allow overriding the type and validation for embedded
|
||||
// types. When that happens, this hack should be revisited.
|
||||
type ObjectMeta struct {
|
||||
// Map of string keys and values that can be used to organize and categorize
|
||||
// (scope and select) objects. May match selectors of replication controllers
|
||||
// and services.
|
||||
// More info: http://kubernetes.io/docs/user-guide/labels
|
||||
// +optional
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations is an unstructured key value map stored with a resource that may be
|
||||
// set by external tools to store and retrieve arbitrary metadata. They are not
|
||||
// queryable and should be preserved when modifying objects.
|
||||
// More info: http://kubernetes.io/docs/user-guide/annotations
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
// ANCHOR: CommonConditions
|
||||
|
||||
// Common ConditionTypes used by Cluster API objects.
|
||||
const (
|
||||
// ReadyCondition defines the Ready condition type that summarizes the operational state of a Cluster API object.
|
||||
ReadyCondition ConditionType = "Ready"
|
||||
)
|
||||
|
||||
// Common ConditionReason used by Cluster API objects.
|
||||
const (
|
||||
// DeletingReason (Severity=Info) documents an condition not in Status=True because the underlying object it is currently being deleted.
|
||||
DeletingReason = "Deleting"
|
||||
|
||||
// DeletionFailedReason (Severity=Warning) documents an condition not in Status=True because the underlying object
|
||||
// encountered problems during deletion. This is a warning because the reconciler will retry deletion.
|
||||
DeletionFailedReason = "DeletionFailed"
|
||||
|
||||
// DeletedReason (Severity=Info) documents an condition not in Status=True because the underlying object was deleted.
|
||||
DeletedReason = "Deleted"
|
||||
|
||||
// IncorrectExternalRefReason (Severity=Error) documents a CAPI object with an incorrect external object reference.
|
||||
IncorrectExternalRefReason = "IncorrectExternalRef"
|
||||
)
|
||||
|
||||
const (
|
||||
// InfrastructureReadyCondition reports a summary of current status of the infrastructure object defined for this cluster/machine/machinepool.
|
||||
// This condition is mirrored from the Ready condition in the infrastructure ref object, and
|
||||
// the absence of this condition might signal problems in the reconcile external loops or the fact that
|
||||
// the infrastructure provider does not implement the Ready condition yet.
|
||||
InfrastructureReadyCondition ConditionType = "InfrastructureReady"
|
||||
|
||||
// WaitingForInfrastructureFallbackReason (Severity=Info) documents a cluster/machine/machinepool waiting for the underlying infrastructure
|
||||
// to be available.
|
||||
// NOTE: This reason is used only as a fallback when the infrastructure object is not reporting its own ready condition.
|
||||
WaitingForInfrastructureFallbackReason = "WaitingForInfrastructure"
|
||||
)
|
||||
|
||||
// ANCHOR_END: CommonConditions
|
||||
|
||||
// Conditions and condition Reasons for the Cluster object.
|
||||
|
||||
const (
|
||||
// ControlPlaneInitializedCondition reports if the cluster's control plane has been initialized such that the
|
||||
// cluster's apiserver is reachable and at least one control plane Machine has a node reference. Once this
|
||||
// condition is marked true, its value is never changed. See the ControlPlaneReady condition for an indication of
|
||||
// the current readiness of the cluster's control plane.
|
||||
ControlPlaneInitializedCondition ConditionType = "ControlPlaneInitialized"
|
||||
|
||||
// MissingNodeRefReason (Severity=Info) documents a cluster waiting for at least one control plane Machine to have
|
||||
// its node reference populated.
|
||||
MissingNodeRefReason = "MissingNodeRef"
|
||||
|
||||
// WaitingForControlPlaneProviderInitializedReason (Severity=Info) documents a cluster waiting for the control plane
|
||||
// provider to report successful control plane initialization.
|
||||
WaitingForControlPlaneProviderInitializedReason = "WaitingForControlPlaneProviderInitialized"
|
||||
|
||||
// ControlPlaneReadyCondition reports the ready condition from the control plane object defined for this cluster.
|
||||
// This condition is mirrored from the Ready condition in the control plane ref object, and
|
||||
// the absence of this condition might signal problems in the reconcile external loops or the fact that
|
||||
// the control plane provider does not implement the Ready condition yet.
|
||||
ControlPlaneReadyCondition ConditionType = "ControlPlaneReady"
|
||||
|
||||
// WaitingForControlPlaneFallbackReason (Severity=Info) documents a cluster waiting for the control plane
|
||||
// to be available.
|
||||
// NOTE: This reason is used only as a fallback when the control plane object is not reporting its own ready condition.
|
||||
WaitingForControlPlaneFallbackReason = "WaitingForControlPlane"
|
||||
|
||||
// WaitingForControlPlaneAvailableReason (Severity=Info) documents a Cluster API object
|
||||
// waiting for the control plane machine to be available.
|
||||
//
|
||||
// NOTE: Having the control plane machine available is a pre-condition for joining additional control planes
|
||||
// or workers nodes.
|
||||
WaitingForControlPlaneAvailableReason = "WaitingForControlPlaneAvailable"
|
||||
)
|
||||
|
||||
// Conditions and condition Reasons for the Machine object.
|
||||
|
||||
const (
|
||||
// BootstrapReadyCondition reports a summary of current status of the bootstrap object defined for this machine.
|
||||
// This condition is mirrored from the Ready condition in the bootstrap ref object, and
|
||||
// the absence of this condition might signal problems in the reconcile external loops or the fact that
|
||||
// the bootstrap provider does not implement the Ready condition yet.
|
||||
BootstrapReadyCondition ConditionType = "BootstrapReady"
|
||||
|
||||
// WaitingForDataSecretFallbackReason (Severity=Info) documents a machine waiting for the bootstrap data secret
|
||||
// to be available.
|
||||
// NOTE: This reason is used only as a fallback when the bootstrap object is not reporting its own ready condition.
|
||||
WaitingForDataSecretFallbackReason = "WaitingForDataSecret"
|
||||
|
||||
// DrainingSucceededCondition provide evidence of the status of the node drain operation which happens during the machine
|
||||
// deletion process.
|
||||
DrainingSucceededCondition ConditionType = "DrainingSucceeded"
|
||||
|
||||
// DrainingReason (Severity=Info) documents a machine node being drained.
|
||||
DrainingReason = "Draining"
|
||||
|
||||
// DrainingFailedReason (Severity=Warning) documents a machine node drain operation failed.
|
||||
DrainingFailedReason = "DrainingFailed"
|
||||
|
||||
// PreDrainDeleteHookSucceededCondition reports a machine waiting for a PreDrainDeleteHook before being delete.
|
||||
PreDrainDeleteHookSucceededCondition ConditionType = "PreDrainDeleteHookSucceeded"
|
||||
|
||||
// PreTerminateDeleteHookSucceededCondition reports a machine waiting for a PreDrainDeleteHook before being delete.
|
||||
PreTerminateDeleteHookSucceededCondition ConditionType = "PreTerminateDeleteHookSucceeded"
|
||||
|
||||
// WaitingExternalHookReason (Severity=Info) provide evidence that we are waiting for an external hook to complete.
|
||||
WaitingExternalHookReason = "WaitingExternalHook"
|
||||
|
||||
// VolumeDetachSucceededCondition reports a machine waiting for volumes to be detached.
|
||||
VolumeDetachSucceededCondition ConditionType = "VolumeDetachSucceeded"
|
||||
|
||||
// WaitingForVolumeDetachReason (Severity=Info) provide evidence that a machine node waiting for volumes to be attached.
|
||||
WaitingForVolumeDetachReason = "WaitingForVolumeDetach"
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineHealthCheckSuccededCondition is set on machines that have passed a healthcheck by the MachineHealthCheck controller.
|
||||
// In the event that the health check fails it will be set to False.
|
||||
MachineHealthCheckSuccededCondition ConditionType = "HealthCheckSucceeded"
|
||||
|
||||
// MachineHasFailureReason is the reason used when a machine has either a FailureReason or a FailureMessage set on its status.
|
||||
MachineHasFailureReason = "MachineHasFailure"
|
||||
|
||||
// NodeStartupTimeoutReason is the reason used when a machine's node does not appear within the specified timeout.
|
||||
NodeStartupTimeoutReason = "NodeStartupTimeout"
|
||||
|
||||
// UnhealthyNodeConditionReason is the reason used when a machine's node has one of the MachineHealthCheck's unhealthy conditions.
|
||||
UnhealthyNodeConditionReason = "UnhealthyNode"
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineOwnerRemediatedCondition is set on machines that have failed a healthcheck by the MachineHealthCheck controller.
|
||||
// MachineOwnerRemediatedCondition is set to False after a health check fails, but should be changed to True by the owning controller after remediation succeeds.
|
||||
MachineOwnerRemediatedCondition ConditionType = "OwnerRemediated"
|
||||
|
||||
// WaitingForRemediationReason is the reason used when a machine fails a health check and remediation is needed.
|
||||
WaitingForRemediationReason = "WaitingForRemediation"
|
||||
|
||||
// RemediationFailedReason is the reason used when a remediation owner fails to remediate an unhealthy machine.
|
||||
RemediationFailedReason = "RemediationFailed"
|
||||
|
||||
// RemediationInProgressReason is the reason used when an unhealthy machine is being remediated by the remediation owner.
|
||||
RemediationInProgressReason = "RemediationInProgress"
|
||||
|
||||
// ExternalRemediationTemplateAvailable is set on machinehealthchecks when MachineHealthCheck controller uses external remediation.
|
||||
// ExternalRemediationTemplateAvailable is set to false if external remediation template is not found.
|
||||
ExternalRemediationTemplateAvailable ConditionType = "ExternalRemediationTemplateAvailable"
|
||||
|
||||
// ExternalRemediationTemplateNotFound is the reason used when a machine health check fails to find external remediation template.
|
||||
ExternalRemediationTemplateNotFound = "ExternalRemediationTemplateNotFound"
|
||||
|
||||
// ExternalRemediationRequestAvailable is set on machinehealthchecks when MachineHealthCheck controller uses external remediation.
|
||||
// ExternalRemediationRequestAvailable is set to false if creating external remediation request fails.
|
||||
ExternalRemediationRequestAvailable ConditionType = "ExternalRemediationRequestAvailable"
|
||||
|
||||
// ExternalRemediationRequestCreationFailed is the reason used when a machine health check fails to create external remediation request.
|
||||
ExternalRemediationRequestCreationFailed = "ExternalRemediationRequestCreationFailed"
|
||||
)
|
||||
|
||||
// Conditions and condition Reasons for the Machine's Node object.
|
||||
const (
|
||||
// MachineNodeHealthyCondition provides info about the operational state of the Kubernetes node hosted on the machine by summarizing node conditions.
|
||||
// If the conditions defined in a Kubernetes node (i.e., NodeReady, NodeMemoryPressure, NodeDiskPressure, NodePIDPressure, and NodeNetworkUnavailable) are in a healthy state, it will be set to True.
|
||||
MachineNodeHealthyCondition ConditionType = "NodeHealthy"
|
||||
|
||||
// WaitingForNodeRefReason (Severity=Info) documents a machine.spec.providerId is not assigned yet.
|
||||
WaitingForNodeRefReason = "WaitingForNodeRef"
|
||||
|
||||
// NodeProvisioningReason (Severity=Info) documents machine in the process of provisioning a node.
|
||||
// NB. provisioning --> NodeRef == "".
|
||||
NodeProvisioningReason = "NodeProvisioning"
|
||||
|
||||
// NodeNotFoundReason (Severity=Error) documents a machine's node has previously been observed but is now gone.
|
||||
// NB. provisioned --> NodeRef != "".
|
||||
NodeNotFoundReason = "NodeNotFound"
|
||||
|
||||
// NodeConditionsFailedReason (Severity=Warning) documents a node is not in a healthy state due to the failed state of at least 1 Kubelet condition.
|
||||
NodeConditionsFailedReason = "NodeConditionsFailed"
|
||||
)
|
||||
|
||||
// Conditions and condition Reasons for the MachineHealthCheck object.
|
||||
|
||||
const (
|
||||
// RemediationAllowedCondition is set on MachineHealthChecks to show the status of whether the MachineHealthCheck is
|
||||
// allowed to remediate any Machines or whether it is blocked from remediating any further.
|
||||
RemediationAllowedCondition ConditionType = "RemediationAllowed"
|
||||
|
||||
// TooManyUnhealthyReason is the reason used when too many Machines are unhealthy and the MachineHealthCheck is blocked
|
||||
// from making any further remediations.
|
||||
TooManyUnhealthyReason = "TooManyUnhealthy"
|
||||
)
|
||||
|
||||
// Conditions and condition Reasons for MachineDeployments.
|
||||
|
||||
const (
|
||||
// MachineDeploymentAvailableCondition means the MachineDeployment is available, that is, at least the minimum available
|
||||
// machines required (i.e. Spec.Replicas-MaxUnavailable when MachineDeploymentStrategyType = RollingUpdate) are up and running for at least minReadySeconds.
|
||||
MachineDeploymentAvailableCondition ConditionType = "Available"
|
||||
|
||||
// WaitingForAvailableMachinesReason (Severity=Warning) reflects the fact that the required minimum number of machines for a machinedeployment are not available.
|
||||
WaitingForAvailableMachinesReason = "WaitingForAvailableMachines"
|
||||
)
|
||||
|
||||
// Conditions and condition Reasons for MachineSets.
|
||||
|
||||
const (
|
||||
// MachinesCreatedCondition documents that the machines controlled by the MachineSet are created.
|
||||
// When this condition is false, it indicates that there was an error when cloning the infrastructure/bootstrap template or
|
||||
// when generating the machine object.
|
||||
MachinesCreatedCondition ConditionType = "MachinesCreated"
|
||||
|
||||
// MachinesReadyCondition reports an aggregate of current status of the machines controlled by the MachineSet.
|
||||
MachinesReadyCondition ConditionType = "MachinesReady"
|
||||
|
||||
// BootstrapTemplateCloningFailedReason (Severity=Error) documents a MachineSet failing to
|
||||
// clone the bootstrap template.
|
||||
BootstrapTemplateCloningFailedReason = "BootstrapTemplateCloningFailed"
|
||||
|
||||
// InfrastructureTemplateCloningFailedReason (Severity=Error) documents a MachineSet failing to
|
||||
// clone the infrastructure template.
|
||||
InfrastructureTemplateCloningFailedReason = "InfrastructureTemplateCloningFailed"
|
||||
|
||||
// MachineCreationFailedReason (Severity=Error) documents a MachineSet failing to
|
||||
// generate a machine object.
|
||||
MachineCreationFailedReason = "MachineCreationFailed"
|
||||
|
||||
// ResizedCondition documents a MachineSet is resizing the set of controlled machines.
|
||||
ResizedCondition ConditionType = "Resized"
|
||||
|
||||
// ScalingUpReason (Severity=Info) documents a MachineSet is increasing the number of replicas.
|
||||
ScalingUpReason = "ScalingUp"
|
||||
|
||||
// ScalingDownReason (Severity=Info) documents a MachineSet is decreasing the number of replicas.
|
||||
ScalingDownReason = "ScalingDown"
|
||||
)
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ANCHOR: ConditionSeverity
|
||||
|
||||
// ConditionSeverity expresses the severity of a Condition Type failing.
|
||||
type ConditionSeverity string
|
||||
|
||||
const (
|
||||
// ConditionSeverityError specifies that a condition with `Status=False` is an error.
|
||||
ConditionSeverityError ConditionSeverity = "Error"
|
||||
|
||||
// ConditionSeverityWarning specifies that a condition with `Status=False` is a warning.
|
||||
ConditionSeverityWarning ConditionSeverity = "Warning"
|
||||
|
||||
// ConditionSeverityInfo specifies that a condition with `Status=False` is informative.
|
||||
ConditionSeverityInfo ConditionSeverity = "Info"
|
||||
|
||||
// ConditionSeverityNone should apply only to conditions with `Status=True`.
|
||||
ConditionSeverityNone ConditionSeverity = ""
|
||||
)
|
||||
|
||||
// ANCHOR_END: ConditionSeverity
|
||||
|
||||
// ANCHOR: ConditionType
|
||||
|
||||
// ConditionType is a valid value for Condition.Type.
|
||||
type ConditionType string
|
||||
|
||||
// ANCHOR_END: ConditionType
|
||||
|
||||
// ANCHOR: Condition
|
||||
|
||||
// Condition defines an observation of a Cluster API resource operational state.
|
||||
type Condition struct {
|
||||
// Type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
// Many .condition.type values are consistent across resources like Available, but because arbitrary conditions
|
||||
// can be useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
// +required
|
||||
Type ConditionType `json:"type"`
|
||||
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
// +required
|
||||
Status corev1.ConditionStatus `json:"status"`
|
||||
|
||||
// Severity provides an explicit classification of Reason code, so the users or machines can immediately
|
||||
// understand the current situation and act accordingly.
|
||||
// The Severity field MUST be set only when Status=False.
|
||||
// +optional
|
||||
Severity ConditionSeverity `json:"severity,omitempty"`
|
||||
|
||||
// Last time the condition transitioned from one status to another.
|
||||
// This should be when the underlying condition changed. If that is not known, then using the time when
|
||||
// the API field changed is acceptable.
|
||||
// +required
|
||||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||
|
||||
// The reason for the condition's last transition in CamelCase.
|
||||
// The specific API may choose whether or not this field is considered a guaranteed API.
|
||||
// This field may not be empty.
|
||||
// +optional
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// A human readable message indicating details about the transition.
|
||||
// This field may be empty.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: Condition
|
||||
|
||||
// ANCHOR: Conditions
|
||||
|
||||
// Conditions provide observations of the operational state of a Cluster API resource.
|
||||
type Conditions []Condition
|
||||
|
||||
// ANCHOR_END: Conditions
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
apiconversion "k8s.io/apimachinery/pkg/conversion"
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
|
||||
)
|
||||
|
||||
func (src *Cluster) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.Cluster)
|
||||
|
||||
if err := Convert_v1alpha4_Cluster_To_v1beta1_Cluster(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Manually restore data.
|
||||
restored := &clusterv1.Cluster{}
|
||||
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if restored.Spec.Topology != nil {
|
||||
if dst.Spec.Topology == nil {
|
||||
dst.Spec.Topology = &clusterv1.Topology{}
|
||||
}
|
||||
dst.Spec.Topology.Variables = restored.Spec.Topology.Variables
|
||||
|
||||
if restored.Spec.Topology.ControlPlane.MachineHealthCheck != nil {
|
||||
dst.Spec.Topology.ControlPlane.MachineHealthCheck = restored.Spec.Topology.ControlPlane.MachineHealthCheck
|
||||
}
|
||||
|
||||
if restored.Spec.Topology.ControlPlane.NodeDrainTimeout != nil {
|
||||
dst.Spec.Topology.ControlPlane.NodeDrainTimeout = restored.Spec.Topology.ControlPlane.NodeDrainTimeout
|
||||
}
|
||||
|
||||
if restored.Spec.Topology.ControlPlane.NodeVolumeDetachTimeout != nil {
|
||||
dst.Spec.Topology.ControlPlane.NodeVolumeDetachTimeout = restored.Spec.Topology.ControlPlane.NodeVolumeDetachTimeout
|
||||
}
|
||||
|
||||
if restored.Spec.Topology.ControlPlane.NodeDeletionTimeout != nil {
|
||||
dst.Spec.Topology.ControlPlane.NodeDeletionTimeout = restored.Spec.Topology.ControlPlane.NodeDeletionTimeout
|
||||
}
|
||||
|
||||
if restored.Spec.Topology.Workers != nil {
|
||||
if dst.Spec.Topology.Workers == nil {
|
||||
dst.Spec.Topology.Workers = &clusterv1.WorkersTopology{}
|
||||
}
|
||||
for i := range restored.Spec.Topology.Workers.MachineDeployments {
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].FailureDomain = restored.Spec.Topology.Workers.MachineDeployments[i].FailureDomain
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].Variables = restored.Spec.Topology.Workers.MachineDeployments[i].Variables
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].NodeDrainTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeDrainTimeout
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].NodeVolumeDetachTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeVolumeDetachTimeout
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].NodeDeletionTimeout = restored.Spec.Topology.Workers.MachineDeployments[i].NodeDeletionTimeout
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].MinReadySeconds = restored.Spec.Topology.Workers.MachineDeployments[i].MinReadySeconds
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].Strategy = restored.Spec.Topology.Workers.MachineDeployments[i].Strategy
|
||||
dst.Spec.Topology.Workers.MachineDeployments[i].MachineHealthCheck = restored.Spec.Topology.Workers.MachineDeployments[i].MachineHealthCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Cluster) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.Cluster)
|
||||
|
||||
if err := Convert_v1beta1_Cluster_To_v1alpha4_Cluster(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve Hub data on down-conversion except for metadata
|
||||
if err := utilconversion.MarshalData(src, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *ClusterList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.ClusterList)
|
||||
|
||||
return Convert_v1alpha4_ClusterList_To_v1beta1_ClusterList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *ClusterList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.ClusterList)
|
||||
|
||||
return Convert_v1beta1_ClusterList_To_v1alpha4_ClusterList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *ClusterClass) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.ClusterClass)
|
||||
|
||||
if err := Convert_v1alpha4_ClusterClass_To_v1beta1_ClusterClass(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Manually restore data.
|
||||
restored := &clusterv1.ClusterClass{}
|
||||
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Spec.Patches = restored.Spec.Patches
|
||||
dst.Spec.Variables = restored.Spec.Variables
|
||||
dst.Spec.ControlPlane.MachineHealthCheck = restored.Spec.ControlPlane.MachineHealthCheck
|
||||
dst.Spec.ControlPlane.NodeDrainTimeout = restored.Spec.ControlPlane.NodeDrainTimeout
|
||||
dst.Spec.ControlPlane.NodeVolumeDetachTimeout = restored.Spec.ControlPlane.NodeVolumeDetachTimeout
|
||||
dst.Spec.ControlPlane.NodeDeletionTimeout = restored.Spec.ControlPlane.NodeDeletionTimeout
|
||||
|
||||
for i := range restored.Spec.Workers.MachineDeployments {
|
||||
dst.Spec.Workers.MachineDeployments[i].MachineHealthCheck = restored.Spec.Workers.MachineDeployments[i].MachineHealthCheck
|
||||
dst.Spec.Workers.MachineDeployments[i].FailureDomain = restored.Spec.Workers.MachineDeployments[i].FailureDomain
|
||||
dst.Spec.Workers.MachineDeployments[i].NodeDrainTimeout = restored.Spec.Workers.MachineDeployments[i].NodeDrainTimeout
|
||||
dst.Spec.Workers.MachineDeployments[i].NodeVolumeDetachTimeout = restored.Spec.Workers.MachineDeployments[i].NodeVolumeDetachTimeout
|
||||
dst.Spec.Workers.MachineDeployments[i].NodeDeletionTimeout = restored.Spec.Workers.MachineDeployments[i].NodeDeletionTimeout
|
||||
dst.Spec.Workers.MachineDeployments[i].MinReadySeconds = restored.Spec.Workers.MachineDeployments[i].MinReadySeconds
|
||||
dst.Spec.Workers.MachineDeployments[i].Strategy = restored.Spec.Workers.MachineDeployments[i].Strategy
|
||||
}
|
||||
|
||||
dst.Status = restored.Status
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ClusterClass) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.ClusterClass)
|
||||
|
||||
if err := Convert_v1beta1_ClusterClass_To_v1alpha4_ClusterClass(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve Hub data on down-conversion except for metadata
|
||||
if err := utilconversion.MarshalData(src, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *ClusterClassList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.ClusterClassList)
|
||||
|
||||
return Convert_v1alpha4_ClusterClassList_To_v1beta1_ClusterClassList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *ClusterClassList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.ClusterClassList)
|
||||
|
||||
return Convert_v1beta1_ClusterClassList_To_v1alpha4_ClusterClassList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *Machine) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.Machine)
|
||||
|
||||
if err := Convert_v1alpha4_Machine_To_v1beta1_Machine(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Manually restore data.
|
||||
restored := &clusterv1.Machine{}
|
||||
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Spec.NodeDeletionTimeout = restored.Spec.NodeDeletionTimeout
|
||||
dst.Status.CertificatesExpiryDate = restored.Status.CertificatesExpiryDate
|
||||
dst.Spec.NodeVolumeDetachTimeout = restored.Spec.NodeVolumeDetachTimeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Machine) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.Machine)
|
||||
|
||||
if err := Convert_v1beta1_Machine_To_v1alpha4_Machine(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve Hub data on down-conversion except for metadata
|
||||
if err := utilconversion.MarshalData(src, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *MachineList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineList)
|
||||
|
||||
return Convert_v1alpha4_MachineList_To_v1beta1_MachineList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *MachineList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineList)
|
||||
|
||||
return Convert_v1beta1_MachineList_To_v1alpha4_MachineList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *MachineSet) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineSet)
|
||||
|
||||
if err := Convert_v1alpha4_MachineSet_To_v1beta1_MachineSet(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Manually restore data.
|
||||
restored := &clusterv1.MachineSet{}
|
||||
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Spec.Template.Spec.NodeDeletionTimeout = restored.Spec.Template.Spec.NodeDeletionTimeout
|
||||
dst.Spec.Template.Spec.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.NodeVolumeDetachTimeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *MachineSet) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineSet)
|
||||
|
||||
if err := Convert_v1beta1_MachineSet_To_v1alpha4_MachineSet(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve Hub data on down-conversion except for metadata
|
||||
return utilconversion.MarshalData(src, dst)
|
||||
}
|
||||
|
||||
func (src *MachineSetList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineSetList)
|
||||
|
||||
return Convert_v1alpha4_MachineSetList_To_v1beta1_MachineSetList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *MachineSetList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineSetList)
|
||||
|
||||
return Convert_v1beta1_MachineSetList_To_v1alpha4_MachineSetList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *MachineDeployment) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineDeployment)
|
||||
|
||||
if err := Convert_v1alpha4_MachineDeployment_To_v1beta1_MachineDeployment(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Manually restore data.
|
||||
restored := &clusterv1.MachineDeployment{}
|
||||
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.Spec.Template.Spec.NodeDeletionTimeout = restored.Spec.Template.Spec.NodeDeletionTimeout
|
||||
dst.Spec.Template.Spec.NodeVolumeDetachTimeout = restored.Spec.Template.Spec.NodeVolumeDetachTimeout
|
||||
dst.Spec.RolloutAfter = restored.Spec.RolloutAfter
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *MachineDeployment) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineDeployment)
|
||||
|
||||
if err := Convert_v1beta1_MachineDeployment_To_v1alpha4_MachineDeployment(src, dst, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Preserve Hub data on down-conversion except for metadata
|
||||
return utilconversion.MarshalData(src, dst)
|
||||
}
|
||||
|
||||
func (src *MachineDeploymentList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineDeploymentList)
|
||||
|
||||
return Convert_v1alpha4_MachineDeploymentList_To_v1beta1_MachineDeploymentList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *MachineDeploymentList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineDeploymentList)
|
||||
|
||||
return Convert_v1beta1_MachineDeploymentList_To_v1alpha4_MachineDeploymentList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *MachineHealthCheck) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineHealthCheck)
|
||||
|
||||
return Convert_v1alpha4_MachineHealthCheck_To_v1beta1_MachineHealthCheck(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *MachineHealthCheck) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineHealthCheck)
|
||||
|
||||
return Convert_v1beta1_MachineHealthCheck_To_v1alpha4_MachineHealthCheck(src, dst, nil)
|
||||
}
|
||||
|
||||
func (src *MachineHealthCheckList) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*clusterv1.MachineHealthCheckList)
|
||||
|
||||
return Convert_v1alpha4_MachineHealthCheckList_To_v1beta1_MachineHealthCheckList(src, dst, nil)
|
||||
}
|
||||
|
||||
func (dst *MachineHealthCheckList) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*clusterv1.MachineHealthCheckList)
|
||||
|
||||
return Convert_v1beta1_MachineHealthCheckList_To_v1alpha4_MachineHealthCheckList(src, dst, nil)
|
||||
}
|
||||
|
||||
func Convert_v1alpha4_MachineStatus_To_v1beta1_MachineStatus(in *MachineStatus, out *clusterv1.MachineStatus, s apiconversion.Scope) error {
|
||||
// Status.version has been removed in v1beta1, thus requiring custom conversion function. the information will be dropped.
|
||||
return autoConvert_v1alpha4_MachineStatus_To_v1beta1_MachineStatus(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_ClusterClassSpec_To_v1alpha4_ClusterClassSpec(in *clusterv1.ClusterClassSpec, out *ClusterClassSpec, s apiconversion.Scope) error {
|
||||
// spec.{variables,patches} has been added with v1beta1.
|
||||
return autoConvert_v1beta1_ClusterClassSpec_To_v1alpha4_ClusterClassSpec(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_MachineSpec_To_v1alpha4_MachineSpec(in *clusterv1.MachineSpec, out *MachineSpec, s apiconversion.Scope) error {
|
||||
// spec.nodeDeletionTimeout has been added with v1beta1.
|
||||
return autoConvert_v1beta1_MachineSpec_To_v1alpha4_MachineSpec(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_MachineDeploymentSpec_To_v1alpha4_MachineDeploymentSpec(in *clusterv1.MachineDeploymentSpec, out *MachineDeploymentSpec, s apiconversion.Scope) error {
|
||||
return autoConvert_v1beta1_MachineDeploymentSpec_To_v1alpha4_MachineDeploymentSpec(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_Topology_To_v1alpha4_Topology(in *clusterv1.Topology, out *Topology, s apiconversion.Scope) error {
|
||||
// spec.topology.variables has been added with v1beta1.
|
||||
return autoConvert_v1beta1_Topology_To_v1alpha4_Topology(in, out, s)
|
||||
}
|
||||
|
||||
// Convert_v1beta1_MachineDeploymentTopology_To_v1alpha4_MachineDeploymentTopology is an autogenerated conversion function.
|
||||
func Convert_v1beta1_MachineDeploymentTopology_To_v1alpha4_MachineDeploymentTopology(in *clusterv1.MachineDeploymentTopology, out *MachineDeploymentTopology, s apiconversion.Scope) error {
|
||||
// MachineDeploymentTopology.FailureDomain has been added with v1beta1.
|
||||
return autoConvert_v1beta1_MachineDeploymentTopology_To_v1alpha4_MachineDeploymentTopology(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_MachineDeploymentClass_To_v1alpha4_MachineDeploymentClass(in *clusterv1.MachineDeploymentClass, out *MachineDeploymentClass, s apiconversion.Scope) error {
|
||||
// machineDeploymentClass.machineHealthCheck has been added with v1beta1.
|
||||
return autoConvert_v1beta1_MachineDeploymentClass_To_v1alpha4_MachineDeploymentClass(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_ControlPlaneClass_To_v1alpha4_ControlPlaneClass(in *clusterv1.ControlPlaneClass, out *ControlPlaneClass, s apiconversion.Scope) error {
|
||||
// controlPlaneClass.machineHealthCheck has been added with v1beta1.
|
||||
return autoConvert_v1beta1_ControlPlaneClass_To_v1alpha4_ControlPlaneClass(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_ControlPlaneTopology_To_v1alpha4_ControlPlaneTopology(in *clusterv1.ControlPlaneTopology, out *ControlPlaneTopology, s apiconversion.Scope) error {
|
||||
// controlPlaneTopology.nodeDrainTimeout has been added with v1beta1.
|
||||
return autoConvert_v1beta1_ControlPlaneTopology_To_v1alpha4_ControlPlaneTopology(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_MachineStatus_To_v1alpha4_MachineStatus(in *clusterv1.MachineStatus, out *MachineStatus, s apiconversion.Scope) error {
|
||||
// MachineStatus.CertificatesExpiryDate has been added in v1beta1.
|
||||
return autoConvert_v1beta1_MachineStatus_To_v1alpha4_MachineStatus(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1beta1_ClusterClass_To_v1alpha4_ClusterClass(in *clusterv1.ClusterClass, out *ClusterClass, s apiconversion.Scope) error {
|
||||
// ClusterClass.Status has been added in v1beta1.
|
||||
return autoConvert_v1beta1_ClusterClass_To_v1alpha4_ClusterClass(in, out, s)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4 contains the v1alpha4 API implementation.
|
||||
// +k8s:conversion-gen=sigs.k8s.io/cluster-api/api/v1beta1
|
||||
//
|
||||
// Deprecated: This package will be removed in one of the next releases.
|
||||
package v1alpha4
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4 contains API Schema definitions for the cluster v1alpha4 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=cluster.x-k8s.io
|
||||
package v1alpha4
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects.
|
||||
GroupVersion = schema.GroupVersion{Group: "cluster.x-k8s.io", Version: "v1alpha4"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
|
||||
localSchemeBuilder = SchemeBuilder.SchemeBuilder
|
||||
)
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
// MachinePhase is a string representation of a Machine Phase.
|
||||
//
|
||||
// This type is a high-level indicator of the status of the Machine as it is provisioned,
|
||||
// from the API user’s perspective.
|
||||
//
|
||||
// The value should not be interpreted by any software components as a reliable indication
|
||||
// of the actual state of the Machine, and controllers should not use the Machine Phase field
|
||||
// value when making decisions about what action to take.
|
||||
//
|
||||
// Controllers should always look at the actual state of the Machine’s fields to make those decisions.
|
||||
type MachinePhase string
|
||||
|
||||
const (
|
||||
// MachinePhasePending is the first state a Machine is assigned by
|
||||
// Cluster API Machine controller after being created.
|
||||
MachinePhasePending = MachinePhase("Pending")
|
||||
|
||||
// MachinePhaseProvisioning is the state when the
|
||||
// Machine infrastructure is being created.
|
||||
MachinePhaseProvisioning = MachinePhase("Provisioning")
|
||||
|
||||
// MachinePhaseProvisioned is the state when its
|
||||
// infrastructure has been created and configured.
|
||||
MachinePhaseProvisioned = MachinePhase("Provisioned")
|
||||
|
||||
// MachinePhaseRunning is the Machine state when it has
|
||||
// become a Kubernetes Node in a Ready state.
|
||||
MachinePhaseRunning = MachinePhase("Running")
|
||||
|
||||
// MachinePhaseDeleting is the Machine state when a delete
|
||||
// request has been sent to the API Server,
|
||||
// but its infrastructure has not yet been fully deleted.
|
||||
MachinePhaseDeleting = MachinePhase("Deleting")
|
||||
|
||||
// MachinePhaseDeleted is the Machine state when the object
|
||||
// and the related infrastructure is deleted and
|
||||
// ready to be garbage collected by the API Server.
|
||||
MachinePhaseDeleted = MachinePhase("Deleted")
|
||||
|
||||
// MachinePhaseFailed is the Machine state when the system
|
||||
// might require user intervention.
|
||||
MachinePhaseFailed = MachinePhase("Failed")
|
||||
|
||||
// MachinePhaseUnknown is returned if the Machine state cannot be determined.
|
||||
MachinePhaseUnknown = MachinePhase("Unknown")
|
||||
)
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
capierrors "sigs.k8s.io/cluster-api/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineFinalizer is set on PrepareForCreate callback.
|
||||
MachineFinalizer = "machine.cluster.x-k8s.io"
|
||||
|
||||
// MachineControlPlaneLabelName is the label set on machines or related objects that are part of a control plane.
|
||||
MachineControlPlaneLabelName = "cluster.x-k8s.io/control-plane"
|
||||
|
||||
// ExcludeNodeDrainingAnnotation annotation explicitly skips node draining if set.
|
||||
ExcludeNodeDrainingAnnotation = "machine.cluster.x-k8s.io/exclude-node-draining"
|
||||
|
||||
// MachineSetLabelName is the label set on machines if they're controlled by MachineSet.
|
||||
MachineSetLabelName = "cluster.x-k8s.io/set-name"
|
||||
|
||||
// MachineDeploymentLabelName is the label set on machines if they're controlled by MachineDeployment.
|
||||
MachineDeploymentLabelName = "cluster.x-k8s.io/deployment-name"
|
||||
|
||||
// PreDrainDeleteHookAnnotationPrefix annotation specifies the prefix we
|
||||
// search each annotation for during the pre-drain.delete lifecycle hook
|
||||
// to pause reconciliation of deletion. These hooks will prevent removal of
|
||||
// draining the associated node until all are removed.
|
||||
PreDrainDeleteHookAnnotationPrefix = "pre-drain.delete.hook.machine.cluster.x-k8s.io"
|
||||
|
||||
// PreTerminateDeleteHookAnnotationPrefix annotation specifies the prefix we
|
||||
// search each annotation for during the pre-terminate.delete lifecycle hook
|
||||
// to pause reconciliation of deletion. These hooks will prevent removal of
|
||||
// an instance from an infrastructure provider until all are removed.
|
||||
PreTerminateDeleteHookAnnotationPrefix = "pre-terminate.delete.hook.machine.cluster.x-k8s.io"
|
||||
)
|
||||
|
||||
// ANCHOR: MachineSpec
|
||||
|
||||
// MachineSpec defines the desired state of Machine.
|
||||
type MachineSpec struct {
|
||||
// ClusterName is the name of the Cluster this object belongs to.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
ClusterName string `json:"clusterName"`
|
||||
|
||||
// Bootstrap is a reference to a local struct which encapsulates
|
||||
// fields to configure the Machine’s bootstrapping mechanism.
|
||||
Bootstrap Bootstrap `json:"bootstrap"`
|
||||
|
||||
// InfrastructureRef is a required reference to a custom resource
|
||||
// offered by an infrastructure provider.
|
||||
InfrastructureRef corev1.ObjectReference `json:"infrastructureRef"`
|
||||
|
||||
// Version defines the desired Kubernetes version.
|
||||
// This field is meant to be optionally used by bootstrap providers.
|
||||
// +optional
|
||||
Version *string `json:"version,omitempty"`
|
||||
|
||||
// ProviderID is the identification ID of the machine provided by the provider.
|
||||
// This field must match the provider ID as seen on the node object corresponding to this machine.
|
||||
// This field is required by higher level consumers of cluster-api. Example use case is cluster autoscaler
|
||||
// with cluster-api as provider. Clean-up logic in the autoscaler compares machines to nodes to find out
|
||||
// machines at provider which could not get registered as Kubernetes nodes. With cluster-api as a
|
||||
// generic out-of-tree provider for autoscaler, this field is required by autoscaler to be
|
||||
// able to have a provider view of the list of machines. Another list of nodes is queried from the k8s apiserver
|
||||
// and then a comparison is done to find out unregistered machines and are marked for delete.
|
||||
// This field will be set by the actuators and consumed by higher level entities like autoscaler that will
|
||||
// be interfacing with cluster-api as generic provider.
|
||||
// +optional
|
||||
ProviderID *string `json:"providerID,omitempty"`
|
||||
|
||||
// FailureDomain is the failure domain the machine will be created in.
|
||||
// Must match a key in the FailureDomains map stored on the cluster object.
|
||||
// +optional
|
||||
FailureDomain *string `json:"failureDomain,omitempty"`
|
||||
|
||||
// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
|
||||
// The default value is 0, meaning that the node can be drained without any time limitations.
|
||||
// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
|
||||
// +optional
|
||||
NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineSpec
|
||||
|
||||
// ANCHOR: MachineStatus
|
||||
|
||||
// MachineStatus defines the observed state of Machine.
|
||||
type MachineStatus struct {
|
||||
// NodeRef will point to the corresponding Node if it exists.
|
||||
// +optional
|
||||
NodeRef *corev1.ObjectReference `json:"nodeRef,omitempty"`
|
||||
|
||||
// NodeInfo is a set of ids/uuids to uniquely identify the node.
|
||||
// More info: https://kubernetes.io/docs/concepts/nodes/node/#info
|
||||
// +optional
|
||||
NodeInfo *corev1.NodeSystemInfo `json:"nodeInfo,omitempty"`
|
||||
|
||||
// LastUpdated identifies when the phase of the Machine last transitioned.
|
||||
// +optional
|
||||
LastUpdated *metav1.Time `json:"lastUpdated,omitempty"`
|
||||
|
||||
// Version specifies the current version of Kubernetes running
|
||||
// on the corresponding Node. This is meant to be a means of bubbling
|
||||
// up status from the Node to the Machine.
|
||||
// It is entirely optional, but useful for end-user UX if it’s present.
|
||||
// +optional
|
||||
Version *string `json:"version,omitempty"`
|
||||
|
||||
// FailureReason will be set in the event that there is a terminal problem
|
||||
// reconciling the Machine and will contain a succinct value suitable
|
||||
// for machine interpretation.
|
||||
//
|
||||
// This field should not be set for transitive errors that a controller
|
||||
// faces that are expected to be fixed automatically over
|
||||
// time (like service outages), but instead indicate that something is
|
||||
// fundamentally wrong with the Machine's spec or the configuration of
|
||||
// the controller, and that manual intervention is required. Examples
|
||||
// of terminal errors would be invalid combinations of settings in the
|
||||
// spec, values that are unsupported by the controller, or the
|
||||
// responsible controller itself being critically misconfigured.
|
||||
//
|
||||
// Any transient errors that occur during the reconciliation of Machines
|
||||
// can be added as events to the Machine object and/or logged in the
|
||||
// controller's output.
|
||||
// +optional
|
||||
FailureReason *capierrors.MachineStatusError `json:"failureReason,omitempty"`
|
||||
|
||||
// FailureMessage will be set in the event that there is a terminal problem
|
||||
// reconciling the Machine and will contain a more verbose string suitable
|
||||
// for logging and human consumption.
|
||||
//
|
||||
// This field should not be set for transitive errors that a controller
|
||||
// faces that are expected to be fixed automatically over
|
||||
// time (like service outages), but instead indicate that something is
|
||||
// fundamentally wrong with the Machine's spec or the configuration of
|
||||
// the controller, and that manual intervention is required. Examples
|
||||
// of terminal errors would be invalid combinations of settings in the
|
||||
// spec, values that are unsupported by the controller, or the
|
||||
// responsible controller itself being critically misconfigured.
|
||||
//
|
||||
// Any transient errors that occur during the reconciliation of Machines
|
||||
// can be added as events to the Machine object and/or logged in the
|
||||
// controller's output.
|
||||
// +optional
|
||||
FailureMessage *string `json:"failureMessage,omitempty"`
|
||||
|
||||
// Addresses is a list of addresses assigned to the machine.
|
||||
// This field is copied from the infrastructure provider reference.
|
||||
// +optional
|
||||
Addresses MachineAddresses `json:"addresses,omitempty"`
|
||||
|
||||
// Phase represents the current phase of machine actuation.
|
||||
// E.g. Pending, Running, Terminating, Failed etc.
|
||||
// +optional
|
||||
Phase string `json:"phase,omitempty"`
|
||||
|
||||
// BootstrapReady is the state of the bootstrap provider.
|
||||
// +optional
|
||||
BootstrapReady bool `json:"bootstrapReady"`
|
||||
|
||||
// InfrastructureReady is the state of the infrastructure provider.
|
||||
// +optional
|
||||
InfrastructureReady bool `json:"infrastructureReady"`
|
||||
|
||||
// ObservedGeneration is the latest generation observed by the controller.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Conditions defines current service state of the Machine.
|
||||
// +optional
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineStatus
|
||||
|
||||
// SetTypedPhase sets the Phase field to the string representation of MachinePhase.
|
||||
func (m *MachineStatus) SetTypedPhase(p MachinePhase) {
|
||||
m.Phase = string(p)
|
||||
}
|
||||
|
||||
// GetTypedPhase attempts to parse the Phase field and return
|
||||
// the typed MachinePhase representation as described in `machine_phase_types.go`.
|
||||
func (m *MachineStatus) GetTypedPhase() MachinePhase {
|
||||
switch phase := MachinePhase(m.Phase); phase {
|
||||
case
|
||||
MachinePhasePending,
|
||||
MachinePhaseProvisioning,
|
||||
MachinePhaseProvisioned,
|
||||
MachinePhaseRunning,
|
||||
MachinePhaseDeleting,
|
||||
MachinePhaseDeleted,
|
||||
MachinePhaseFailed:
|
||||
return phase
|
||||
default:
|
||||
return MachinePhaseUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// ANCHOR: Bootstrap
|
||||
|
||||
// Bootstrap encapsulates fields to configure the Machine’s bootstrapping mechanism.
|
||||
type Bootstrap struct {
|
||||
// ConfigRef is a reference to a bootstrap provider-specific resource
|
||||
// that holds configuration details. The reference is optional to
|
||||
// allow users/operators to specify Bootstrap.DataSecretName without
|
||||
// the need of a controller.
|
||||
// +optional
|
||||
ConfigRef *corev1.ObjectReference `json:"configRef,omitempty"`
|
||||
|
||||
// DataSecretName is the name of the secret that stores the bootstrap data script.
|
||||
// If nil, the Machine should remain in the Pending state.
|
||||
// +optional
|
||||
DataSecretName *string `json:"dataSecretName,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: Bootstrap
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=machines,shortName=ma,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".spec.clusterName",description="Cluster"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Machine"
|
||||
// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="Provider ID"
|
||||
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Machine status such as Terminating/Pending/Running/Failed etc"
|
||||
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.version",description="Kubernetes version associated with this Machine"
|
||||
// +kubebuilder:printcolumn:name="NodeName",type="string",JSONPath=".status.nodeRef.name",description="Node name associated with this machine",priority=1
|
||||
|
||||
// Machine is the Schema for the machines API.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type Machine struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec MachineSpec `json:"spec,omitempty"`
|
||||
Status MachineStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GetConditions returns the set of conditions for this object.
|
||||
func (m *Machine) GetConditions() Conditions {
|
||||
return m.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions sets the conditions on this object.
|
||||
func (m *Machine) SetConditions(conditions Conditions) {
|
||||
m.Status.Conditions = conditions
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// MachineList contains a list of Machine.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Machine `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Machine{}, &MachineList{})
|
||||
}
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineDeploymentTopologyFinalizer is the finalizer used by the topology MachineDeployment controller to
|
||||
// clean up referenced template resources if necessary when a MachineDeployment is being deleted.
|
||||
MachineDeploymentTopologyFinalizer = "machinedeployment.topology.cluster.x-k8s.io"
|
||||
)
|
||||
|
||||
// MachineDeploymentStrategyType defines the type of MachineDeployment rollout strategies.
|
||||
type MachineDeploymentStrategyType string
|
||||
|
||||
const (
|
||||
// RollingUpdateMachineDeploymentStrategyType replaces the old MachineSet by new one using rolling update
|
||||
// i.e. gradually scale down the old MachineSet and scale up the new one.
|
||||
RollingUpdateMachineDeploymentStrategyType MachineDeploymentStrategyType = "RollingUpdate"
|
||||
|
||||
// OnDeleteMachineDeploymentStrategyType replaces old MachineSets when the deletion of the associated machines are completed.
|
||||
OnDeleteMachineDeploymentStrategyType MachineDeploymentStrategyType = "OnDelete"
|
||||
|
||||
// RevisionAnnotation is the revision annotation of a machine deployment's machine sets which records its rollout sequence.
|
||||
RevisionAnnotation = "machinedeployment.clusters.x-k8s.io/revision"
|
||||
|
||||
// RevisionHistoryAnnotation maintains the history of all old revisions that a machine set has served for a machine deployment.
|
||||
RevisionHistoryAnnotation = "machinedeployment.clusters.x-k8s.io/revision-history"
|
||||
|
||||
// DesiredReplicasAnnotation is the desired replicas for a machine deployment recorded as an annotation
|
||||
// in its machine sets. Helps in separating scaling events from the rollout process and for
|
||||
// determining if the new machine set for a deployment is really saturated.
|
||||
DesiredReplicasAnnotation = "machinedeployment.clusters.x-k8s.io/desired-replicas"
|
||||
|
||||
// MaxReplicasAnnotation is the maximum replicas a deployment can have at a given point, which
|
||||
// is machinedeployment.spec.replicas + maxSurge. Used by the underlying machine sets to estimate their
|
||||
// proportions in case the deployment has surge replicas.
|
||||
MaxReplicasAnnotation = "machinedeployment.clusters.x-k8s.io/max-replicas"
|
||||
|
||||
// MachineDeploymentUniqueLabel is the label applied to Machines
|
||||
// in a MachineDeployment containing the hash of the template.
|
||||
MachineDeploymentUniqueLabel = "machine-template-hash"
|
||||
)
|
||||
|
||||
// ANCHOR: MachineDeploymentSpec
|
||||
|
||||
// MachineDeploymentSpec defines the desired state of MachineDeployment.
|
||||
type MachineDeploymentSpec struct {
|
||||
// ClusterName is the name of the Cluster this object belongs to.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
ClusterName string `json:"clusterName"`
|
||||
|
||||
// Number of desired machines. Defaults to 1.
|
||||
// This is a pointer to distinguish between explicit zero and not specified.
|
||||
// +optional
|
||||
// +kubebuilder:default=1
|
||||
Replicas *int32 `json:"replicas,omitempty"`
|
||||
|
||||
// Label selector for machines. Existing MachineSets whose machines are
|
||||
// selected by this will be the ones affected by this deployment.
|
||||
// It must match the machine template's labels.
|
||||
Selector metav1.LabelSelector `json:"selector"`
|
||||
|
||||
// Template describes the machines that will be created.
|
||||
Template MachineTemplateSpec `json:"template"`
|
||||
|
||||
// The deployment strategy to use to replace existing machines with
|
||||
// new ones.
|
||||
// +optional
|
||||
Strategy *MachineDeploymentStrategy `json:"strategy,omitempty"`
|
||||
|
||||
// Minimum number of seconds for which a newly created machine should
|
||||
// be ready.
|
||||
// Defaults to 0 (machine will be considered available as soon as it
|
||||
// is ready)
|
||||
// +optional
|
||||
MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`
|
||||
|
||||
// The number of old MachineSets to retain to allow rollback.
|
||||
// This is a pointer to distinguish between explicit zero and not specified.
|
||||
// Defaults to 1.
|
||||
// +optional
|
||||
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"`
|
||||
|
||||
// Indicates that the deployment is paused.
|
||||
// +optional
|
||||
Paused bool `json:"paused,omitempty"`
|
||||
|
||||
// The maximum time in seconds for a deployment to make progress before it
|
||||
// is considered to be failed. The deployment controller will continue to
|
||||
// process failed deployments and a condition with a ProgressDeadlineExceeded
|
||||
// reason will be surfaced in the deployment status. Note that progress will
|
||||
// not be estimated during the time a deployment is paused. Defaults to 600s.
|
||||
ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineDeploymentSpec
|
||||
|
||||
// ANCHOR: MachineDeploymentStrategy
|
||||
|
||||
// MachineDeploymentStrategy describes how to replace existing machines
|
||||
// with new ones.
|
||||
type MachineDeploymentStrategy struct {
|
||||
// Type of deployment.
|
||||
// Default is RollingUpdate.
|
||||
// +kubebuilder:validation:Enum=RollingUpdate;OnDelete
|
||||
// +optional
|
||||
Type MachineDeploymentStrategyType `json:"type,omitempty"`
|
||||
|
||||
// Rolling update config params. Present only if
|
||||
// MachineDeploymentStrategyType = RollingUpdate.
|
||||
// +optional
|
||||
RollingUpdate *MachineRollingUpdateDeployment `json:"rollingUpdate,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineDeploymentStrategy
|
||||
|
||||
// ANCHOR: MachineRollingUpdateDeployment
|
||||
|
||||
// MachineRollingUpdateDeployment is used to control the desired behavior of rolling update.
|
||||
type MachineRollingUpdateDeployment struct {
|
||||
// The maximum number of machines that can be unavailable during the update.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of desired
|
||||
// machines (ex: 10%).
|
||||
// Absolute number is calculated from percentage by rounding down.
|
||||
// This can not be 0 if MaxSurge is 0.
|
||||
// Defaults to 0.
|
||||
// Example: when this is set to 30%, the old MachineSet can be scaled
|
||||
// down to 70% of desired machines immediately when the rolling update
|
||||
// starts. Once new machines are ready, old MachineSet can be scaled
|
||||
// down further, followed by scaling up the new MachineSet, ensuring
|
||||
// that the total number of machines available at all times
|
||||
// during the update is at least 70% of desired machines.
|
||||
// +optional
|
||||
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
|
||||
|
||||
// The maximum number of machines that can be scheduled above the
|
||||
// desired number of machines.
|
||||
// Value can be an absolute number (ex: 5) or a percentage of
|
||||
// desired machines (ex: 10%).
|
||||
// This can not be 0 if MaxUnavailable is 0.
|
||||
// Absolute number is calculated from percentage by rounding up.
|
||||
// Defaults to 1.
|
||||
// Example: when this is set to 30%, the new MachineSet can be scaled
|
||||
// up immediately when the rolling update starts, such that the total
|
||||
// number of old and new machines do not exceed 130% of desired
|
||||
// machines. Once old machines have been killed, new MachineSet can
|
||||
// be scaled up further, ensuring that total number of machines running
|
||||
// at any time during the update is at most 130% of desired machines.
|
||||
// +optional
|
||||
MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty"`
|
||||
|
||||
// DeletePolicy defines the policy used by the MachineDeployment to identify nodes to delete when downscaling.
|
||||
// Valid values are "Random, "Newest", "Oldest"
|
||||
// When no value is supplied, the default DeletePolicy of MachineSet is used
|
||||
// +kubebuilder:validation:Enum=Random;Newest;Oldest
|
||||
// +optional
|
||||
DeletePolicy *string `json:"deletePolicy,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineRollingUpdateDeployment
|
||||
|
||||
// ANCHOR: MachineDeploymentStatus
|
||||
|
||||
// MachineDeploymentStatus defines the observed state of MachineDeployment.
|
||||
type MachineDeploymentStatus struct {
|
||||
// The generation observed by the deployment controller.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Selector is the same as the label selector but in the string format to avoid introspection
|
||||
// by clients. The string will be in the same format as the query-param syntax.
|
||||
// More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors
|
||||
// +optional
|
||||
Selector string `json:"selector,omitempty"`
|
||||
|
||||
// Total number of non-terminated machines targeted by this deployment
|
||||
// (their labels match the selector).
|
||||
// +optional
|
||||
Replicas int32 `json:"replicas,omitempty"`
|
||||
|
||||
// Total number of non-terminated machines targeted by this deployment
|
||||
// that have the desired template spec.
|
||||
// +optional
|
||||
UpdatedReplicas int32 `json:"updatedReplicas,omitempty"`
|
||||
|
||||
// Total number of ready machines targeted by this deployment.
|
||||
// +optional
|
||||
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
||||
|
||||
// Total number of available machines (ready for at least minReadySeconds)
|
||||
// targeted by this deployment.
|
||||
// +optional
|
||||
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||
|
||||
// Total number of unavailable machines targeted by this deployment.
|
||||
// This is the total number of machines that are still required for
|
||||
// the deployment to have 100% available capacity. They may either
|
||||
// be machines that are running but not yet available or machines
|
||||
// that still have not been created.
|
||||
// +optional
|
||||
UnavailableReplicas int32 `json:"unavailableReplicas,omitempty"`
|
||||
|
||||
// Phase represents the current phase of a MachineDeployment (ScalingUp, ScalingDown, Running, Failed, or Unknown).
|
||||
// +optional
|
||||
Phase string `json:"phase,omitempty"`
|
||||
|
||||
// Conditions defines current service state of the MachineDeployment.
|
||||
// +optional
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineDeploymentStatus
|
||||
|
||||
// MachineDeploymentPhase indicates the progress of the machine deployment.
|
||||
type MachineDeploymentPhase string
|
||||
|
||||
const (
|
||||
// MachineDeploymentPhaseScalingUp indicates the MachineDeployment is scaling up.
|
||||
MachineDeploymentPhaseScalingUp = MachineDeploymentPhase("ScalingUp")
|
||||
|
||||
// MachineDeploymentPhaseScalingDown indicates the MachineDeployment is scaling down.
|
||||
MachineDeploymentPhaseScalingDown = MachineDeploymentPhase("ScalingDown")
|
||||
|
||||
// MachineDeploymentPhaseRunning indicates scaling has completed and all Machines are running.
|
||||
MachineDeploymentPhaseRunning = MachineDeploymentPhase("Running")
|
||||
|
||||
// MachineDeploymentPhaseFailed indicates there was a problem scaling and user intervention might be required.
|
||||
MachineDeploymentPhaseFailed = MachineDeploymentPhase("Failed")
|
||||
|
||||
// MachineDeploymentPhaseUnknown indicates the state of the MachineDeployment cannot be determined.
|
||||
MachineDeploymentPhaseUnknown = MachineDeploymentPhase("Unknown")
|
||||
)
|
||||
|
||||
// SetTypedPhase sets the Phase field to the string representation of MachineDeploymentPhase.
|
||||
func (md *MachineDeploymentStatus) SetTypedPhase(p MachineDeploymentPhase) {
|
||||
md.Phase = string(p)
|
||||
}
|
||||
|
||||
// GetTypedPhase attempts to parse the Phase field and return
|
||||
// the typed MachineDeploymentPhase representation.
|
||||
func (md *MachineDeploymentStatus) GetTypedPhase() MachineDeploymentPhase {
|
||||
switch phase := MachineDeploymentPhase(md.Phase); phase {
|
||||
case
|
||||
MachineDeploymentPhaseScalingDown,
|
||||
MachineDeploymentPhaseScalingUp,
|
||||
MachineDeploymentPhaseRunning,
|
||||
MachineDeploymentPhaseFailed:
|
||||
return phase
|
||||
default:
|
||||
return MachineDeploymentPhaseUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=machinedeployments,shortName=md,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
|
||||
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".spec.clusterName",description="Cluster"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of MachineDeployment"
|
||||
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="MachineDeployment status such as ScalingUp/ScalingDown/Running/Failed/Unknown"
|
||||
// +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".status.replicas",description="Total number of non-terminated machines targeted by this MachineDeployment"
|
||||
// +kubebuilder:printcolumn:name="Ready",type="integer",JSONPath=".status.readyReplicas",description="Total number of ready machines targeted by this MachineDeployment"
|
||||
// +kubebuilder:printcolumn:name="Updated",type=integer,JSONPath=".status.updatedReplicas",description="Total number of non-terminated machines targeted by this deployment that have the desired template spec"
|
||||
// +kubebuilder:printcolumn:name="Unavailable",type=integer,JSONPath=".status.unavailableReplicas",description="Total number of unavailable machines targeted by this MachineDeployment"
|
||||
|
||||
// MachineDeployment is the Schema for the machinedeployments API.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineDeployment struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec MachineDeploymentSpec `json:"spec,omitempty"`
|
||||
Status MachineDeploymentStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// MachineDeploymentList contains a list of MachineDeployment.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineDeploymentList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []MachineDeployment `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&MachineDeployment{}, &MachineDeploymentList{})
|
||||
}
|
||||
|
||||
// GetConditions returns the set of conditions for the machinedeployment.
|
||||
func (m *MachineDeployment) GetConditions() Conditions {
|
||||
return m.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions updates the set of conditions on the machinedeployment.
|
||||
func (m *MachineDeployment) SetConditions(conditions Conditions) {
|
||||
m.Status.Conditions = conditions
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// ANCHOR: MachineHealthCheckSpec
|
||||
|
||||
// MachineHealthCheckSpec defines the desired state of MachineHealthCheck.
|
||||
type MachineHealthCheckSpec struct {
|
||||
// ClusterName is the name of the Cluster this object belongs to.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
ClusterName string `json:"clusterName"`
|
||||
|
||||
// Label selector to match machines whose health will be exercised
|
||||
Selector metav1.LabelSelector `json:"selector"`
|
||||
|
||||
// UnhealthyConditions contains a list of the conditions that determine
|
||||
// whether a node is considered unhealthy. The conditions are combined in a
|
||||
// logical OR, i.e. if any of the conditions is met, the node is unhealthy.
|
||||
//
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
UnhealthyConditions []UnhealthyCondition `json:"unhealthyConditions"`
|
||||
|
||||
// Any further remediation is only allowed if at most "MaxUnhealthy" machines selected by
|
||||
// "selector" are not healthy.
|
||||
// +optional
|
||||
MaxUnhealthy *intstr.IntOrString `json:"maxUnhealthy,omitempty"`
|
||||
|
||||
// Any further remediation is only allowed if the number of machines selected by "selector" as not healthy
|
||||
// is within the range of "UnhealthyRange". Takes precedence over MaxUnhealthy.
|
||||
// Eg. "[3-5]" - This means that remediation will be allowed only when:
|
||||
// (a) there are at least 3 unhealthy machines (and)
|
||||
// (b) there are at most 5 unhealthy machines
|
||||
// +optional
|
||||
// +kubebuilder:validation:Pattern=^\[[0-9]+-[0-9]+\]$
|
||||
UnhealthyRange *string `json:"unhealthyRange,omitempty"`
|
||||
|
||||
// Machines older than this duration without a node will be considered to have
|
||||
// failed and will be remediated.
|
||||
// If not set, this value is defaulted to 10 minutes.
|
||||
// If you wish to disable this feature, set the value explicitly to 0.
|
||||
// +optional
|
||||
NodeStartupTimeout *metav1.Duration `json:"nodeStartupTimeout,omitempty"`
|
||||
|
||||
// RemediationTemplate is a reference to a remediation template
|
||||
// provided by an infrastructure provider.
|
||||
//
|
||||
// This field is completely optional, when filled, the MachineHealthCheck controller
|
||||
// creates a new object from the template referenced and hands off remediation of the machine to
|
||||
// a controller that lives outside of Cluster API.
|
||||
// +optional
|
||||
RemediationTemplate *corev1.ObjectReference `json:"remediationTemplate,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineHealthCHeckSpec
|
||||
|
||||
// ANCHOR: UnhealthyCondition
|
||||
|
||||
// UnhealthyCondition represents a Node condition type and value with a timeout
|
||||
// specified as a duration. When the named condition has been in the given
|
||||
// status for at least the timeout value, a node is considered unhealthy.
|
||||
type UnhealthyCondition struct {
|
||||
// +kubebuilder:validation:Type=string
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Type corev1.NodeConditionType `json:"type"`
|
||||
|
||||
// +kubebuilder:validation:Type=string
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Status corev1.ConditionStatus `json:"status"`
|
||||
|
||||
Timeout metav1.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: UnhealthyCondition
|
||||
|
||||
// ANCHOR: MachineHealthCheckStatus
|
||||
|
||||
// MachineHealthCheckStatus defines the observed state of MachineHealthCheck.
|
||||
type MachineHealthCheckStatus struct {
|
||||
// total number of machines counted by this machine health check
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
ExpectedMachines int32 `json:"expectedMachines,omitempty"`
|
||||
|
||||
// total number of healthy machines counted by this machine health check
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
CurrentHealthy int32 `json:"currentHealthy,omitempty"`
|
||||
|
||||
// RemediationsAllowed is the number of further remediations allowed by this machine health check before
|
||||
// maxUnhealthy short circuiting will be applied
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
RemediationsAllowed int32 `json:"remediationsAllowed,omitempty"`
|
||||
|
||||
// ObservedGeneration is the latest generation observed by the controller.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Targets shows the current list of machines the machine health check is watching
|
||||
// +optional
|
||||
Targets []string `json:"targets,omitempty"`
|
||||
|
||||
// Conditions defines current service state of the MachineHealthCheck.
|
||||
// +optional
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineHealthCheckStatus
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=machinehealthchecks,shortName=mhc;mhcs,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".spec.clusterName",description="Cluster"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of MachineHealthCheck"
|
||||
// +kubebuilder:printcolumn:name="MaxUnhealthy",type="string",JSONPath=".spec.maxUnhealthy",description="Maximum number of unhealthy machines allowed"
|
||||
// +kubebuilder:printcolumn:name="ExpectedMachines",type="integer",JSONPath=".status.expectedMachines",description="Number of machines currently monitored"
|
||||
// +kubebuilder:printcolumn:name="CurrentHealthy",type="integer",JSONPath=".status.currentHealthy",description="Current observed healthy machines"
|
||||
|
||||
// MachineHealthCheck is the Schema for the machinehealthchecks API.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineHealthCheck struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Specification of machine health check policy
|
||||
Spec MachineHealthCheckSpec `json:"spec,omitempty"`
|
||||
|
||||
// Most recently observed status of MachineHealthCheck resource
|
||||
Status MachineHealthCheckStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GetConditions returns the set of conditions for this object.
|
||||
func (m *MachineHealthCheck) GetConditions() Conditions {
|
||||
return m.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions sets the conditions on this object.
|
||||
func (m *MachineHealthCheck) SetConditions(conditions Conditions) {
|
||||
m.Status.Conditions = conditions
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// MachineHealthCheckList contains a list of MachineHealthCheck.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineHealthCheckList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []MachineHealthCheck `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&MachineHealthCheck{}, &MachineHealthCheckList{})
|
||||
}
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 v1alpha4
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
capierrors "sigs.k8s.io/cluster-api/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// MachineSetTopologyFinalizer is the finalizer used by the topology MachineDeployment controller to
|
||||
// clean up referenced template resources if necessary when a MachineSet is being deleted.
|
||||
MachineSetTopologyFinalizer = "machineset.topology.cluster.x-k8s.io"
|
||||
)
|
||||
|
||||
// ANCHOR: MachineSetSpec
|
||||
|
||||
// MachineSetSpec defines the desired state of MachineSet.
|
||||
type MachineSetSpec struct {
|
||||
// ClusterName is the name of the Cluster this object belongs to.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
ClusterName string `json:"clusterName"`
|
||||
|
||||
// Replicas is the number of desired replicas.
|
||||
// This is a pointer to distinguish between explicit zero and unspecified.
|
||||
// Defaults to 1.
|
||||
// +optional
|
||||
// +kubebuilder:default=1
|
||||
Replicas *int32 `json:"replicas,omitempty"`
|
||||
|
||||
// MinReadySeconds is the minimum number of seconds for which a newly created machine should be ready.
|
||||
// Defaults to 0 (machine will be considered available as soon as it is ready)
|
||||
// +optional
|
||||
MinReadySeconds int32 `json:"minReadySeconds,omitempty"`
|
||||
|
||||
// DeletePolicy defines the policy used to identify nodes to delete when downscaling.
|
||||
// Defaults to "Random". Valid values are "Random, "Newest", "Oldest"
|
||||
// +kubebuilder:validation:Enum=Random;Newest;Oldest
|
||||
DeletePolicy string `json:"deletePolicy,omitempty"`
|
||||
|
||||
// Selector is a label query over machines that should match the replica count.
|
||||
// Label keys and values that must match in order to be controlled by this MachineSet.
|
||||
// It must match the machine template's labels.
|
||||
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
Selector metav1.LabelSelector `json:"selector"`
|
||||
|
||||
// Template is the object that describes the machine that will be created if
|
||||
// insufficient replicas are detected.
|
||||
// Object references to custom resources are treated as templates.
|
||||
// +optional
|
||||
Template MachineTemplateSpec `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineSetSpec
|
||||
|
||||
// ANCHOR: MachineTemplateSpec
|
||||
|
||||
// MachineTemplateSpec describes the data needed to create a Machine from a template.
|
||||
type MachineTemplateSpec struct {
|
||||
// Standard object's metadata.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
// +optional
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Specification of the desired behavior of the machine.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
// +optional
|
||||
Spec MachineSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineTemplateSpec
|
||||
|
||||
// MachineSetDeletePolicy defines how priority is assigned to nodes to delete when
|
||||
// downscaling a MachineSet. Defaults to "Random".
|
||||
type MachineSetDeletePolicy string
|
||||
|
||||
const (
|
||||
// RandomMachineSetDeletePolicy prioritizes both Machines that have the annotation
|
||||
// "cluster.x-k8s.io/delete-machine=yes" and Machines that are unhealthy
|
||||
// (Status.FailureReason or Status.FailureMessage are set to a non-empty value).
|
||||
// Finally, it picks Machines at random to delete.
|
||||
RandomMachineSetDeletePolicy MachineSetDeletePolicy = "Random"
|
||||
|
||||
// NewestMachineSetDeletePolicy prioritizes both Machines that have the annotation
|
||||
// "cluster.x-k8s.io/delete-machine=yes" and Machines that are unhealthy
|
||||
// (Status.FailureReason or Status.FailureMessage are set to a non-empty value).
|
||||
// It then prioritizes the newest Machines for deletion based on the Machine's CreationTimestamp.
|
||||
NewestMachineSetDeletePolicy MachineSetDeletePolicy = "Newest"
|
||||
|
||||
// OldestMachineSetDeletePolicy prioritizes both Machines that have the annotation
|
||||
// "cluster.x-k8s.io/delete-machine=yes" and Machines that are unhealthy
|
||||
// (Status.FailureReason or Status.FailureMessage are set to a non-empty value).
|
||||
// It then prioritizes the oldest Machines for deletion based on the Machine's CreationTimestamp.
|
||||
OldestMachineSetDeletePolicy MachineSetDeletePolicy = "Oldest"
|
||||
)
|
||||
|
||||
// ANCHOR: MachineSetStatus
|
||||
|
||||
// MachineSetStatus defines the observed state of MachineSet.
|
||||
type MachineSetStatus struct {
|
||||
// Selector is the same as the label selector but in the string format to avoid introspection
|
||||
// by clients. The string will be in the same format as the query-param syntax.
|
||||
// More info about label selectors: http://kubernetes.io/docs/user-guide/labels#label-selectors
|
||||
// +optional
|
||||
Selector string `json:"selector,omitempty"`
|
||||
|
||||
// Replicas is the most recently observed number of replicas.
|
||||
// +optional
|
||||
Replicas int32 `json:"replicas,omitempty"`
|
||||
|
||||
// The number of replicas that have labels matching the labels of the machine template of the MachineSet.
|
||||
// +optional
|
||||
FullyLabeledReplicas int32 `json:"fullyLabeledReplicas,omitempty"`
|
||||
|
||||
// The number of ready replicas for this MachineSet. A machine is considered ready when the node has been created and is "Ready".
|
||||
// +optional
|
||||
ReadyReplicas int32 `json:"readyReplicas,omitempty"`
|
||||
|
||||
// The number of available replicas (ready for at least minReadySeconds) for this MachineSet.
|
||||
// +optional
|
||||
AvailableReplicas int32 `json:"availableReplicas,omitempty"`
|
||||
|
||||
// ObservedGeneration reflects the generation of the most recently observed MachineSet.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// In the event that there is a terminal problem reconciling the
|
||||
// replicas, both FailureReason and FailureMessage will be set. FailureReason
|
||||
// will be populated with a succinct value suitable for machine
|
||||
// interpretation, while FailureMessage will contain a more verbose
|
||||
// string suitable for logging and human consumption.
|
||||
//
|
||||
// These fields should not be set for transitive errors that a
|
||||
// controller faces that are expected to be fixed automatically over
|
||||
// time (like service outages), but instead indicate that something is
|
||||
// fundamentally wrong with the MachineTemplate's spec or the configuration of
|
||||
// the machine controller, and that manual intervention is required. Examples
|
||||
// of terminal errors would be invalid combinations of settings in the
|
||||
// spec, values that are unsupported by the machine controller, or the
|
||||
// responsible machine controller itself being critically misconfigured.
|
||||
//
|
||||
// Any transient errors that occur during the reconciliation of Machines
|
||||
// can be added as events to the MachineSet object and/or logged in the
|
||||
// controller's output.
|
||||
// +optional
|
||||
FailureReason *capierrors.MachineSetStatusError `json:"failureReason,omitempty"`
|
||||
// +optional
|
||||
FailureMessage *string `json:"failureMessage,omitempty"`
|
||||
// Conditions defines current service state of the MachineSet.
|
||||
// +optional
|
||||
Conditions Conditions `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// ANCHOR_END: MachineSetStatus
|
||||
|
||||
// Validate validates the MachineSet fields.
|
||||
func (m *MachineSet) Validate() field.ErrorList {
|
||||
errors := field.ErrorList{}
|
||||
|
||||
// validate spec.selector and spec.template.labels
|
||||
fldPath := field.NewPath("spec")
|
||||
errors = append(errors, metav1validation.ValidateLabelSelector(&m.Spec.Selector, metav1validation.LabelSelectorValidationOptions{}, fldPath.Child("selector"))...)
|
||||
if len(m.Spec.Selector.MatchLabels)+len(m.Spec.Selector.MatchExpressions) == 0 {
|
||||
errors = append(errors, field.Invalid(fldPath.Child("selector"), m.Spec.Selector, "empty selector is not valid for MachineSet."))
|
||||
}
|
||||
selector, err := metav1.LabelSelectorAsSelector(&m.Spec.Selector)
|
||||
if err != nil {
|
||||
errors = append(errors, field.Invalid(fldPath.Child("selector"), m.Spec.Selector, "invalid label selector."))
|
||||
} else {
|
||||
labels := labels.Set(m.Spec.Template.Labels)
|
||||
if !selector.Matches(labels) {
|
||||
errors = append(errors, field.Invalid(fldPath.Child("template", "metadata", "labels"), m.Spec.Template.Labels, "`selector` does not match template `labels`"))
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=machinesets,shortName=ms,scope=Namespaced,categories=cluster-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector
|
||||
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".spec.clusterName",description="Cluster"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of MachineSet"
|
||||
// +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".status.replicas",description="Total number of non-terminated machines targeted by this machineset"
|
||||
// +kubebuilder:printcolumn:name="Available",type="integer",JSONPath=".status.availableReplicas",description="Total number of available machines (ready for at least minReadySeconds)"
|
||||
// +kubebuilder:printcolumn:name="Ready",type="integer",JSONPath=".status.readyReplicas",description="Total number of ready machines targeted by this machineset."
|
||||
|
||||
// MachineSet is the Schema for the machinesets API.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineSet struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec MachineSetSpec `json:"spec,omitempty"`
|
||||
Status MachineSetStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// MachineSetList contains a list of MachineSet.
|
||||
//
|
||||
// Deprecated: This type will be removed in one of the next releases.
|
||||
type MachineSetList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []MachineSet `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&MachineSet{}, &MachineSetList{})
|
||||
}
|
||||
|
||||
// GetConditions returns the set of conditions for the MachineSet.
|
||||
func (m *MachineSet) GetConditions() Conditions {
|
||||
return m.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions updates the set of conditions on the MachineSet.
|
||||
func (m *MachineSet) SetConditions(conditions Conditions) {
|
||||
m.Status.Conditions = conditions
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 annotations implements annotation helper functions.
|
||||
package annotations
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
)
|
||||
|
||||
// IsPaused returns true if the Cluster is paused or the object has the `paused` annotation.
|
||||
func IsPaused(cluster *clusterv1.Cluster, o metav1.Object) bool {
|
||||
if cluster.Spec.Paused {
|
||||
return true
|
||||
}
|
||||
return HasPaused(o)
|
||||
}
|
||||
|
||||
// IsExternallyManaged returns true if the object has the `managed-by` annotation.
|
||||
func IsExternallyManaged(o metav1.Object) bool {
|
||||
return hasAnnotation(o, clusterv1.ManagedByAnnotation)
|
||||
}
|
||||
|
||||
// HasPaused returns true if the object has the `paused` annotation.
|
||||
func HasPaused(o metav1.Object) bool {
|
||||
return hasAnnotation(o, clusterv1.PausedAnnotation)
|
||||
}
|
||||
|
||||
// HasSkipRemediation returns true if the object has the `skip-remediation` annotation.
|
||||
func HasSkipRemediation(o metav1.Object) bool {
|
||||
return hasAnnotation(o, clusterv1.MachineSkipRemediationAnnotation)
|
||||
}
|
||||
|
||||
// HasWithPrefix returns true if at least one of the annotations has the prefix specified.
|
||||
func HasWithPrefix(prefix string, annotations map[string]string) bool {
|
||||
for key := range annotations {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReplicasManagedByExternalAutoscaler returns true if the standard annotation for external autoscaler is present.
|
||||
func ReplicasManagedByExternalAutoscaler(o metav1.Object) bool {
|
||||
return hasTruthyAnnotationValue(o, clusterv1.ReplicasManagedByAnnotation)
|
||||
}
|
||||
|
||||
// AddAnnotations sets the desired annotations on the object and returns true if the annotations have changed.
|
||||
func AddAnnotations(o metav1.Object, desired map[string]string) bool {
|
||||
if len(desired) == 0 {
|
||||
return false
|
||||
}
|
||||
annotations := o.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
o.SetAnnotations(annotations)
|
||||
}
|
||||
hasChanged := false
|
||||
for k, v := range desired {
|
||||
if cur, ok := annotations[k]; !ok || cur != v {
|
||||
annotations[k] = v
|
||||
hasChanged = true
|
||||
}
|
||||
}
|
||||
return hasChanged
|
||||
}
|
||||
|
||||
// hasAnnotation returns true if the object has the specified annotation.
|
||||
func hasAnnotation(o metav1.Object, annotation string) bool {
|
||||
annotations := o.GetAnnotations()
|
||||
if annotations == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := annotations[annotation]
|
||||
return ok
|
||||
}
|
||||
|
||||
// hasTruthyAnnotationValue returns true if the object has an annotation with a value that is not "false".
|
||||
func hasTruthyAnnotationValue(o metav1.Object, annotation string) bool {
|
||||
annotations := o.GetAnnotations()
|
||||
if annotations == nil {
|
||||
return false
|
||||
}
|
||||
if val, ok := annotations[annotation]; ok {
|
||||
return val != "false"
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 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 contract
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gobuffalo/flect"
|
||||
)
|
||||
|
||||
// CalculateCRDName generates a CRD name based on group and kind according to
|
||||
// the naming conventions in the contract.
|
||||
func CalculateCRDName(group, kind string) string {
|
||||
return fmt.Sprintf("%s.%s", flect.Pluralize(strings.ToLower(kind)), group)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
Copyright 2022 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 contract contains utils related to the Cluster API contract.
|
||||
package contract
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 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 conversion implements conversion utilities.
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
fuzz "github.com/google/gofuzz"
|
||||
"github.com/onsi/gomega"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
"sigs.k8s.io/cluster-api/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// DataAnnotation is the annotation that conversion webhooks
|
||||
// use to retain the data in case of down-conversion from the hub.
|
||||
DataAnnotation = "cluster.x-k8s.io/conversion-data"
|
||||
)
|
||||
|
||||
var (
|
||||
contract = clusterv1.GroupVersion.String()
|
||||
)
|
||||
|
||||
// UpdateReferenceAPIContract takes a client and object reference, queries the API Server for
|
||||
// the Custom Resource Definition and looks which one is the stored version available.
|
||||
//
|
||||
// The object passed as input is modified in place if an updated compatible version is found.
|
||||
// NOTE: This version depends on CRDs being named correctly as defined by contract.CalculateCRDName.
|
||||
func UpdateReferenceAPIContract(ctx context.Context, c client.Client, ref *corev1.ObjectReference) error {
|
||||
gvk := ref.GroupVersionKind()
|
||||
|
||||
metadata, err := util.GetGVKMetadata(ctx, c, gvk)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update apiVersion in ref")
|
||||
}
|
||||
|
||||
chosen, err := getLatestAPIVersionFromContract(metadata)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update apiVersion in ref")
|
||||
}
|
||||
|
||||
// Modify the GroupVersionKind with the new version.
|
||||
if gvk.Version != chosen {
|
||||
gvk.Version = chosen
|
||||
ref.SetGroupVersionKind(gvk)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLatestAPIVersionFromContract(metadata metav1.Object) (string, error) {
|
||||
labels := metadata.GetLabels()
|
||||
|
||||
// If there is no label, return early without changing the reference.
|
||||
supportedVersions, ok := labels[contract]
|
||||
if !ok || supportedVersions == "" {
|
||||
return "", errors.Errorf("cannot find any versions matching contract %q for CRD %v as contract version label(s) are either missing or empty (see https://cluster-api.sigs.k8s.io/developer/providers/contracts.html#api-version-labels)", contract, metadata.GetName())
|
||||
}
|
||||
|
||||
// Pick the latest version in the slice and validate it.
|
||||
kubeVersions := util.KubeAwareAPIVersions(strings.Split(supportedVersions, "_"))
|
||||
sort.Sort(kubeVersions)
|
||||
return kubeVersions[len(kubeVersions)-1], nil
|
||||
}
|
||||
|
||||
// MarshalData stores the source object as json data in the destination object annotations map.
|
||||
// It ignores the metadata of the source object.
|
||||
func MarshalData(src metav1.Object, dst metav1.Object) error {
|
||||
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(u, "metadata")
|
||||
|
||||
data, err := json.Marshal(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations := dst.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
annotations[DataAnnotation] = string(data)
|
||||
dst.SetAnnotations(annotations)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalData tries to retrieve the data from the annotation and unmarshals it into the object passed as input.
|
||||
func UnmarshalData(from metav1.Object, to interface{}) (bool, error) {
|
||||
annotations := from.GetAnnotations()
|
||||
data, ok := annotations[DataAnnotation]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
if err := json.Unmarshal([]byte(data), to); err != nil {
|
||||
return false, err
|
||||
}
|
||||
delete(annotations, DataAnnotation)
|
||||
from.SetAnnotations(annotations)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetFuzzer returns a new fuzzer to be used for testing.
|
||||
func GetFuzzer(scheme *runtime.Scheme, funcs ...fuzzer.FuzzerFuncs) *fuzz.Fuzzer {
|
||||
funcs = append([]fuzzer.FuzzerFuncs{
|
||||
metafuzzer.Funcs,
|
||||
func(_ runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
// Custom fuzzer for metav1.Time pointers which weren't
|
||||
// fuzzed and always resulted in `nil` values.
|
||||
// This implementation is somewhat similar to the one provided
|
||||
// in the metafuzzer.Funcs.
|
||||
func(input *metav1.Time, c fuzz.Continue) {
|
||||
if input != nil {
|
||||
var sec, nsec uint32
|
||||
c.Fuzz(&sec)
|
||||
c.Fuzz(&nsec)
|
||||
fuzzed := metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy()
|
||||
input.Time = fuzzed.Time
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}, funcs...)
|
||||
return fuzzer.FuzzerFor(
|
||||
fuzzer.MergeFuzzerFuncs(funcs...),
|
||||
rand.NewSource(rand.Int63()), //nolint:gosec
|
||||
runtimeserializer.NewCodecFactory(scheme),
|
||||
)
|
||||
}
|
||||
|
||||
// FuzzTestFuncInput contains input parameters
|
||||
// for the FuzzTestFunc function.
|
||||
type FuzzTestFuncInput struct {
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
Hub conversion.Hub
|
||||
HubAfterMutation func(conversion.Hub)
|
||||
|
||||
Spoke conversion.Convertible
|
||||
SpokeAfterMutation func(convertible conversion.Convertible)
|
||||
SkipSpokeAnnotationCleanup bool
|
||||
|
||||
FuzzerFuncs []fuzzer.FuzzerFuncs
|
||||
}
|
||||
|
||||
// FuzzTestFunc returns a new testing function to be used in tests to make sure conversions between
|
||||
// the Hub version of an object and an older version aren't lossy.
|
||||
func FuzzTestFunc(input FuzzTestFuncInput) func(*testing.T) {
|
||||
if input.Scheme == nil {
|
||||
input.Scheme = scheme.Scheme
|
||||
}
|
||||
|
||||
return func(t *testing.T) {
|
||||
t.Helper()
|
||||
t.Run("spoke-hub-spoke", func(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
fuzzer := GetFuzzer(input.Scheme, input.FuzzerFuncs...)
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
// Create the spoke and fuzz it
|
||||
spokeBefore := input.Spoke.DeepCopyObject().(conversion.Convertible)
|
||||
fuzzer.Fuzz(spokeBefore)
|
||||
|
||||
// First convert spoke to hub
|
||||
hubCopy := input.Hub.DeepCopyObject().(conversion.Hub)
|
||||
g.Expect(spokeBefore.ConvertTo(hubCopy)).To(gomega.Succeed())
|
||||
|
||||
// Convert hub back to spoke and check if the resulting spoke is equal to the spoke before the round trip
|
||||
spokeAfter := input.Spoke.DeepCopyObject().(conversion.Convertible)
|
||||
g.Expect(spokeAfter.ConvertFrom(hubCopy)).To(gomega.Succeed())
|
||||
|
||||
// Remove data annotation eventually added by ConvertFrom for avoiding data loss in hub-spoke-hub round trips
|
||||
// NOTE: There are use case when we want to skip this operation, e.g. if the spoke object does not have ObjectMeta (e.g. kubeadm types).
|
||||
if !input.SkipSpokeAnnotationCleanup {
|
||||
metaAfter := spokeAfter.(metav1.Object)
|
||||
delete(metaAfter.GetAnnotations(), DataAnnotation)
|
||||
}
|
||||
|
||||
if input.SpokeAfterMutation != nil {
|
||||
input.SpokeAfterMutation(spokeAfter)
|
||||
}
|
||||
|
||||
g.Expect(apiequality.Semantic.DeepEqual(spokeBefore, spokeAfter)).To(gomega.BeTrue(), cmp.Diff(spokeBefore, spokeAfter))
|
||||
}
|
||||
})
|
||||
t.Run("hub-spoke-hub", func(t *testing.T) {
|
||||
g := gomega.NewWithT(t)
|
||||
fuzzer := GetFuzzer(input.Scheme, input.FuzzerFuncs...)
|
||||
|
||||
for i := 0; i < 10000; i++ {
|
||||
// Create the hub and fuzz it
|
||||
hubBefore := input.Hub.DeepCopyObject().(conversion.Hub)
|
||||
fuzzer.Fuzz(hubBefore)
|
||||
|
||||
// First convert hub to spoke
|
||||
dstCopy := input.Spoke.DeepCopyObject().(conversion.Convertible)
|
||||
g.Expect(dstCopy.ConvertFrom(hubBefore)).To(gomega.Succeed())
|
||||
|
||||
// Convert spoke back to hub and check if the resulting hub is equal to the hub before the round trip
|
||||
hubAfter := input.Hub.DeepCopyObject().(conversion.Hub)
|
||||
g.Expect(dstCopy.ConvertTo(hubAfter)).To(gomega.Succeed())
|
||||
|
||||
if input.HubAfterMutation != nil {
|
||||
input.HubAfterMutation(hubAfter)
|
||||
}
|
||||
|
||||
g.Expect(apiequality.Semantic.DeepEqual(hubBefore, hubAfter)).To(gomega.BeTrue(), cmp.Diff(hubBefore, hubAfter))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
backoffSteps = 10
|
||||
backoffFactor = 1.25
|
||||
backoffDuration = 5
|
||||
backoffJitter = 1.0
|
||||
)
|
||||
|
||||
// Retry retries a given function with exponential backoff.
|
||||
func Retry(fn wait.ConditionFunc, initialBackoffSec int) error {
|
||||
if initialBackoffSec <= 0 {
|
||||
initialBackoffSec = backoffDuration
|
||||
}
|
||||
backoffConfig := wait.Backoff{
|
||||
Steps: backoffSteps,
|
||||
Factor: backoffFactor,
|
||||
Duration: time.Duration(initialBackoffSec) * time.Second,
|
||||
Jitter: backoffJitter,
|
||||
}
|
||||
retryErr := wait.ExponentialBackoff(backoffConfig, fn)
|
||||
if retryErr != nil {
|
||||
return retryErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Poll tries a condition func until it returns true, an error, or the timeout
|
||||
// is reached.
|
||||
func Poll(interval, timeout time.Duration, condition wait.ConditionFunc) error {
|
||||
return wait.Poll(interval, timeout, condition)
|
||||
}
|
||||
|
||||
// PollImmediate tries a condition func until it returns true, an error, or the timeout
|
||||
// is reached.
|
||||
func PollImmediate(interval, timeout time.Duration, condition wait.ConditionFunc) error {
|
||||
return wait.PollImmediate(interval, timeout, condition)
|
||||
}
|
|
@ -1,597 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 util implements utilities.
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
k8sversion "k8s.io/apimachinery/pkg/version"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
|
||||
"sigs.k8s.io/cluster-api/util/annotations"
|
||||
"sigs.k8s.io/cluster-api/util/contract"
|
||||
)
|
||||
|
||||
const (
|
||||
// CharSet defines the alphanumeric set for random string generation.
|
||||
CharSet = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
)
|
||||
|
||||
var (
|
||||
rnd = rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec
|
||||
|
||||
// ErrNoCluster is returned when the cluster
|
||||
// label could not be found on the object passed in.
|
||||
ErrNoCluster = fmt.Errorf("no %q label present", clusterv1.ClusterNameLabel)
|
||||
|
||||
// ErrUnstructuredFieldNotFound determines that a field
|
||||
// in an unstructured object could not be found.
|
||||
ErrUnstructuredFieldNotFound = fmt.Errorf("field not found")
|
||||
)
|
||||
|
||||
// RandomString returns a random alphanumeric string.
|
||||
func RandomString(n int) string {
|
||||
result := make([]byte, n)
|
||||
for i := range result {
|
||||
result[i] = CharSet[rnd.Intn(len(CharSet))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// Ordinalize takes an int and returns the ordinalized version of it.
|
||||
// Eg. 1 --> 1st, 103 --> 103rd.
|
||||
func Ordinalize(n int) string {
|
||||
m := map[int]string{
|
||||
0: "th",
|
||||
1: "st",
|
||||
2: "nd",
|
||||
3: "rd",
|
||||
4: "th",
|
||||
5: "th",
|
||||
6: "th",
|
||||
7: "th",
|
||||
8: "th",
|
||||
9: "th",
|
||||
}
|
||||
|
||||
an := int(math.Abs(float64(n)))
|
||||
if an < 10 {
|
||||
return fmt.Sprintf("%d%s", n, m[an])
|
||||
}
|
||||
return fmt.Sprintf("%d%s", n, m[an%10])
|
||||
}
|
||||
|
||||
// IsExternalManagedControlPlane returns a bool indicating whether the control plane referenced
|
||||
// in the passed Unstructured resource is an externally managed control plane such as AKS, EKS, GKE, etc.
|
||||
func IsExternalManagedControlPlane(controlPlane *unstructured.Unstructured) bool {
|
||||
managed, found, err := unstructured.NestedBool(controlPlane.Object, "status", "externalManagedControlPlane")
|
||||
if err != nil || !found {
|
||||
return false
|
||||
}
|
||||
return managed
|
||||
}
|
||||
|
||||
// GetMachineIfExists gets a machine from the API server if it exists.
|
||||
func GetMachineIfExists(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.Machine, error) {
|
||||
if c == nil {
|
||||
// Being called before k8s is setup as part of control plane VM creation
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Machines are identified by name
|
||||
machine := &clusterv1.Machine{}
|
||||
err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, machine)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return machine, nil
|
||||
}
|
||||
|
||||
// IsControlPlaneMachine checks machine is a control plane node.
|
||||
func IsControlPlaneMachine(machine *clusterv1.Machine) bool {
|
||||
_, ok := machine.ObjectMeta.Labels[clusterv1.MachineControlPlaneLabel]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsNodeReady returns true if a node is ready.
|
||||
func IsNodeReady(node *corev1.Node) bool {
|
||||
for _, condition := range node.Status.Conditions {
|
||||
if condition.Type == corev1.NodeReady {
|
||||
return condition.Status == corev1.ConditionTrue
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetClusterFromMetadata returns the Cluster object (if present) using the object metadata.
|
||||
func GetClusterFromMetadata(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.Cluster, error) {
|
||||
if obj.Labels[clusterv1.ClusterNameLabel] == "" {
|
||||
return nil, errors.WithStack(ErrNoCluster)
|
||||
}
|
||||
return GetClusterByName(ctx, c, obj.Namespace, obj.Labels[clusterv1.ClusterNameLabel])
|
||||
}
|
||||
|
||||
// GetOwnerCluster returns the Cluster object owning the current resource.
|
||||
func GetOwnerCluster(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.Cluster, error) {
|
||||
for _, ref := range obj.GetOwnerReferences() {
|
||||
if ref.Kind != "Cluster" {
|
||||
continue
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
if gv.Group == clusterv1.GroupVersion.Group {
|
||||
return GetClusterByName(ctx, c, obj.Namespace, ref.Name)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetClusterByName finds and return a Cluster object using the specified params.
|
||||
func GetClusterByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.Cluster, error) {
|
||||
cluster := &clusterv1.Cluster{}
|
||||
key := client.ObjectKey{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
if err := c.Get(ctx, key, cluster); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get Cluster/%s", name)
|
||||
}
|
||||
|
||||
return cluster, nil
|
||||
}
|
||||
|
||||
// ObjectKey returns client.ObjectKey for the object.
|
||||
func ObjectKey(object metav1.Object) client.ObjectKey {
|
||||
return client.ObjectKey{
|
||||
Namespace: object.GetNamespace(),
|
||||
Name: object.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterToInfrastructureMapFunc returns a handler.ToRequestsFunc that watches for
|
||||
// Cluster events and returns reconciliation requests for an infrastructure provider object.
|
||||
func ClusterToInfrastructureMapFunc(ctx context.Context, gvk schema.GroupVersionKind, c client.Client, providerCluster client.Object) handler.MapFunc {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
return func(o client.Object) []reconcile.Request {
|
||||
cluster, ok := o.(*clusterv1.Cluster)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return early if the InfrastructureRef is nil.
|
||||
if cluster.Spec.InfrastructureRef == nil {
|
||||
return nil
|
||||
}
|
||||
gk := gvk.GroupKind()
|
||||
// Return early if the GroupKind doesn't match what we expect.
|
||||
infraGK := cluster.Spec.InfrastructureRef.GroupVersionKind().GroupKind()
|
||||
if gk != infraGK {
|
||||
return nil
|
||||
}
|
||||
providerCluster := providerCluster.DeepCopyObject().(client.Object)
|
||||
key := types.NamespacedName{Namespace: cluster.Namespace, Name: cluster.Spec.InfrastructureRef.Name}
|
||||
|
||||
if err := c.Get(ctx, key, providerCluster); err != nil {
|
||||
log.V(4).Error(err, fmt.Sprintf("Failed to get %T", providerCluster))
|
||||
return nil
|
||||
}
|
||||
|
||||
if annotations.IsExternallyManaged(providerCluster) {
|
||||
log.V(4).Info(fmt.Sprintf("%T is externally managed, skipping mapping", providerCluster))
|
||||
return nil
|
||||
}
|
||||
|
||||
return []reconcile.Request{
|
||||
{
|
||||
NamespacedName: client.ObjectKey{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: cluster.Spec.InfrastructureRef.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetOwnerMachine returns the Machine object owning the current resource.
|
||||
func GetOwnerMachine(ctx context.Context, c client.Client, obj metav1.ObjectMeta) (*clusterv1.Machine, error) {
|
||||
for _, ref := range obj.GetOwnerReferences() {
|
||||
gv, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ref.Kind == "Machine" && gv.Group == clusterv1.GroupVersion.Group {
|
||||
return GetMachineByName(ctx, c, obj.Namespace, ref.Name)
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetMachineByName finds and return a Machine object using the specified params.
|
||||
func GetMachineByName(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.Machine, error) {
|
||||
m := &clusterv1.Machine{}
|
||||
key := client.ObjectKey{Name: name, Namespace: namespace}
|
||||
if err := c.Get(ctx, key, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// MachineToInfrastructureMapFunc returns a handler.ToRequestsFunc that watches for
|
||||
// Machine events and returns reconciliation requests for an infrastructure provider object.
|
||||
func MachineToInfrastructureMapFunc(gvk schema.GroupVersionKind) handler.MapFunc {
|
||||
return func(o client.Object) []reconcile.Request {
|
||||
m, ok := o.(*clusterv1.Machine)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
gk := gvk.GroupKind()
|
||||
// Return early if the GroupKind doesn't match what we expect.
|
||||
infraGK := m.Spec.InfrastructureRef.GroupVersionKind().GroupKind()
|
||||
if gk != infraGK {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []reconcile.Request{
|
||||
{
|
||||
NamespacedName: client.ObjectKey{
|
||||
Namespace: m.Namespace,
|
||||
Name: m.Spec.InfrastructureRef.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HasOwnerRef returns true if the OwnerReference is already in the slice. It matches based on Group, Kind and Name.
|
||||
func HasOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) bool {
|
||||
return indexOwnerRef(ownerReferences, ref) > -1
|
||||
}
|
||||
|
||||
// EnsureOwnerRef makes sure the slice contains the OwnerReference.
|
||||
// Note: EnsureOwnerRef will update the version of the OwnerReference fi it exists with a different version. It will also update the UID.
|
||||
func EnsureOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) []metav1.OwnerReference {
|
||||
idx := indexOwnerRef(ownerReferences, ref)
|
||||
if idx == -1 {
|
||||
return append(ownerReferences, ref)
|
||||
}
|
||||
ownerReferences[idx] = ref
|
||||
return ownerReferences
|
||||
}
|
||||
|
||||
// ReplaceOwnerRef re-parents an object from one OwnerReference to another
|
||||
// It compares strictly based on UID to avoid reparenting across an intentional deletion: if an object is deleted
|
||||
// and re-created with the same name and namespace, the only way to tell there was an in-progress deletion
|
||||
// is by comparing the UIDs.
|
||||
func ReplaceOwnerRef(ownerReferences []metav1.OwnerReference, source metav1.Object, target metav1.OwnerReference) []metav1.OwnerReference {
|
||||
fi := -1
|
||||
for index, r := range ownerReferences {
|
||||
if r.UID == source.GetUID() {
|
||||
fi = index
|
||||
ownerReferences[index] = target
|
||||
break
|
||||
}
|
||||
}
|
||||
if fi < 0 {
|
||||
ownerReferences = append(ownerReferences, target)
|
||||
}
|
||||
return ownerReferences
|
||||
}
|
||||
|
||||
// RemoveOwnerRef returns the slice of owner references after removing the supplied owner ref.
|
||||
// Note: RemoveOwnerRef ignores apiVersion and UID. It will remove the passed ownerReference where it matches Name, Group and Kind.
|
||||
func RemoveOwnerRef(ownerReferences []metav1.OwnerReference, inputRef metav1.OwnerReference) []metav1.OwnerReference {
|
||||
if index := indexOwnerRef(ownerReferences, inputRef); index != -1 {
|
||||
return append(ownerReferences[:index], ownerReferences[index+1:]...)
|
||||
}
|
||||
return ownerReferences
|
||||
}
|
||||
|
||||
// indexOwnerRef returns the index of the owner reference in the slice if found, or -1.
|
||||
func indexOwnerRef(ownerReferences []metav1.OwnerReference, ref metav1.OwnerReference) int {
|
||||
for index, r := range ownerReferences {
|
||||
if referSameObject(r, ref) {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// IsOwnedByObject returns true if any of the owner references point to the given target.
|
||||
// It matches the object based on the Group, Kind and Name.
|
||||
func IsOwnedByObject(obj metav1.Object, target client.Object) bool {
|
||||
for _, ref := range obj.GetOwnerReferences() {
|
||||
ref := ref
|
||||
if refersTo(&ref, target) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsControlledBy differs from metav1.IsControlledBy. This function matches on Group, Kind and Name. The metav1.IsControlledBy function matches on UID only.
|
||||
func IsControlledBy(obj metav1.Object, owner client.Object) bool {
|
||||
controllerRef := metav1.GetControllerOfNoCopy(obj)
|
||||
if controllerRef == nil {
|
||||
return false
|
||||
}
|
||||
return refersTo(controllerRef, owner)
|
||||
}
|
||||
|
||||
// Returns true if a and b point to the same object based on Group, Kind and Name.
|
||||
func referSameObject(a, b metav1.OwnerReference) bool {
|
||||
aGV, err := schema.ParseGroupVersion(a.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
bGV, err := schema.ParseGroupVersion(b.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return aGV.Group == bGV.Group && a.Kind == b.Kind && a.Name == b.Name
|
||||
}
|
||||
|
||||
// Returns true if ref refers to obj based on Group, Kind and Name.
|
||||
func refersTo(ref *metav1.OwnerReference, obj client.Object) bool {
|
||||
refGv, err := schema.ParseGroupVersion(ref.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
return refGv.Group == gvk.Group && ref.Kind == gvk.Kind && ref.Name == obj.GetName()
|
||||
}
|
||||
|
||||
// UnstructuredUnmarshalField is a wrapper around json and unstructured objects to decode and copy a specific field
|
||||
// value into an object.
|
||||
func UnstructuredUnmarshalField(obj *unstructured.Unstructured, v interface{}, fields ...string) error {
|
||||
if obj == nil || obj.Object == nil {
|
||||
return errors.Errorf("failed to unmarshal unstructured object: object is nil")
|
||||
}
|
||||
|
||||
value, found, err := unstructured.NestedFieldNoCopy(obj.Object, fields...)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to retrieve field %q from %q", strings.Join(fields, "."), obj.GroupVersionKind())
|
||||
}
|
||||
if !found || value == nil {
|
||||
return ErrUnstructuredFieldNotFound
|
||||
}
|
||||
valueBytes, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to json-encode field %q value from %q", strings.Join(fields, "."), obj.GroupVersionKind())
|
||||
}
|
||||
if err := json.Unmarshal(valueBytes, v); err != nil {
|
||||
return errors.Wrapf(err, "failed to json-decode field %q value from %q", strings.Join(fields, "."), obj.GroupVersionKind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasOwner checks if any of the references in the passed list match the given group from apiVersion and one of the given kinds.
|
||||
func HasOwner(refList []metav1.OwnerReference, apiVersion string, kinds []string) bool {
|
||||
gv, err := schema.ParseGroupVersion(apiVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
kindMap := make(map[string]bool)
|
||||
for _, kind := range kinds {
|
||||
kindMap[kind] = true
|
||||
}
|
||||
|
||||
for _, mr := range refList {
|
||||
mrGroupVersion, err := schema.ParseGroupVersion(mr.APIVersion)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if mrGroupVersion.Group == gv.Group && kindMap[mr.Kind] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetGVKMetadata retrieves a CustomResourceDefinition metadata from the API server using partial object metadata.
|
||||
//
|
||||
// This function is greatly more efficient than GetCRDWithContract and should be preferred in most cases.
|
||||
func GetGVKMetadata(ctx context.Context, c client.Client, gvk schema.GroupVersionKind) (*metav1.PartialObjectMetadata, error) {
|
||||
meta := &metav1.PartialObjectMetadata{}
|
||||
meta.SetName(contract.CalculateCRDName(gvk.Group, gvk.Kind))
|
||||
meta.SetGroupVersionKind(apiextensionsv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
|
||||
if err := c.Get(ctx, client.ObjectKeyFromObject(meta), meta); err != nil {
|
||||
return meta, errors.Wrap(err, "failed to retrieve metadata from GVK resource")
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// KubeAwareAPIVersions is a sortable slice of kube-like version strings.
|
||||
//
|
||||
// Kube-like version strings are starting with a v, followed by a major version,
|
||||
// optional "alpha" or "beta" strings followed by a minor version (e.g. v1, v2beta1).
|
||||
// Versions will be sorted based on GA/alpha/beta first and then major and minor
|
||||
// versions. e.g. v2, v1, v1beta2, v1beta1, v1alpha1.
|
||||
type KubeAwareAPIVersions []string
|
||||
|
||||
func (k KubeAwareAPIVersions) Len() int { return len(k) }
|
||||
func (k KubeAwareAPIVersions) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
|
||||
func (k KubeAwareAPIVersions) Less(i, j int) bool {
|
||||
return k8sversion.CompareKubeAwareVersionStrings(k[i], k[j]) < 0
|
||||
}
|
||||
|
||||
// ClusterToObjectsMapper returns a mapper function that gets a cluster and lists all objects for the object passed in
|
||||
// and returns a list of requests.
|
||||
// NB: The objects are required to have `clusterv1.ClusterNameLabel` applied.
|
||||
func ClusterToObjectsMapper(c client.Client, ro client.ObjectList, scheme *runtime.Scheme) (handler.MapFunc, error) {
|
||||
gvk, err := apiutil.GVKForObject(ro, scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isNamespaced, err := isAPINamespaced(gvk, c.RESTMapper())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(o client.Object) []ctrl.Request {
|
||||
cluster, ok := o.(*clusterv1.Cluster)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
listOpts := []client.ListOption{
|
||||
client.MatchingLabels{
|
||||
clusterv1.ClusterNameLabel: cluster.Name,
|
||||
},
|
||||
}
|
||||
|
||||
if isNamespaced {
|
||||
listOpts = append(listOpts, client.InNamespace(cluster.Namespace))
|
||||
}
|
||||
|
||||
list := &unstructured.UnstructuredList{}
|
||||
list.SetGroupVersionKind(gvk)
|
||||
if err := c.List(context.TODO(), list, listOpts...); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
results := []ctrl.Request{}
|
||||
for _, obj := range list.Items {
|
||||
results = append(results, ctrl.Request{
|
||||
NamespacedName: client.ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()},
|
||||
})
|
||||
}
|
||||
return results
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isAPINamespaced detects if a GroupVersionKind is namespaced.
|
||||
func isAPINamespaced(gk schema.GroupVersionKind, restmapper meta.RESTMapper) (bool, error) {
|
||||
restMapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gk.Group, Kind: gk.Kind})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get restmapping: %w", err)
|
||||
}
|
||||
|
||||
switch restMapping.Scope.Name() {
|
||||
case "":
|
||||
return false, errors.New("Scope cannot be identified. Empty scope returned")
|
||||
case meta.RESTScopeNameRoot:
|
||||
return false, nil
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectReferenceToUnstructured converts an object reference to an unstructured object.
|
||||
func ObjectReferenceToUnstructured(in corev1.ObjectReference) *unstructured.Unstructured {
|
||||
out := &unstructured.Unstructured{}
|
||||
out.SetKind(in.Kind)
|
||||
out.SetAPIVersion(in.APIVersion)
|
||||
out.SetNamespace(in.Namespace)
|
||||
out.SetName(in.Name)
|
||||
return out
|
||||
}
|
||||
|
||||
// IsSupportedVersionSkew will return true if a and b are no more than one minor version off from each other.
|
||||
func IsSupportedVersionSkew(a, b semver.Version) bool {
|
||||
if a.Major != b.Major {
|
||||
return false
|
||||
}
|
||||
if a.Minor > b.Minor {
|
||||
return a.Minor-b.Minor == 1
|
||||
}
|
||||
return b.Minor-a.Minor <= 1
|
||||
}
|
||||
|
||||
// LowestNonZeroResult compares two reconciliation results
|
||||
// and returns the one with lowest requeue time.
|
||||
func LowestNonZeroResult(i, j ctrl.Result) ctrl.Result {
|
||||
switch {
|
||||
case i.IsZero():
|
||||
return j
|
||||
case j.IsZero():
|
||||
return i
|
||||
case i.Requeue:
|
||||
return i
|
||||
case j.Requeue:
|
||||
return j
|
||||
case i.RequeueAfter < j.RequeueAfter:
|
||||
return i
|
||||
default:
|
||||
return j
|
||||
}
|
||||
}
|
||||
|
||||
// LowestNonZeroInt32 returns the lowest non-zero value of the two provided values.
|
||||
func LowestNonZeroInt32(i, j int32) int32 {
|
||||
if i == 0 {
|
||||
return j
|
||||
}
|
||||
if j == 0 {
|
||||
return i
|
||||
}
|
||||
if i < j {
|
||||
return i
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
// IsNil returns an error if the passed interface is equal to nil or if it has an interface value of nil.
|
||||
func IsNil(i interface{}) bool {
|
||||
if i == nil {
|
||||
return true
|
||||
}
|
||||
switch reflect.TypeOf(i).Kind() {
|
||||
case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice, reflect.Interface, reflect.UnsafePointer, reflect.Func:
|
||||
return reflect.ValueOf(i).IsValid() && reflect.ValueOf(i).IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue