chore: implement DeepCopy for machine configuration

Resources code extensively uses DeepCopy to prevent in-memory copy of
the resource to be mutated outside of the resource model.

Previous implementation relied on YAML serialization to copy the
machine configuration which was slow, potentially might lead to panics
and it generates pressure on garbage collection.

This implementation uses k8s code generator to generate DeepCopy methods
with some manual helpers when code generator can't handle it.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2021-07-07 18:12:53 +03:00 committed by talos-bot
parent fe4ed3c734
commit d930a26502
17 changed files with 1428 additions and 31 deletions

View File

@ -42,6 +42,7 @@ policies:
- .pb.go
- _string.go
- _string_linux.go
- zz_generated.deepcopy.go
header: |
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -101,6 +101,9 @@ RUN go install mvdan.cc/gofumpt/gofumports@${GOFUMPT_VERSION} \
ARG STRINGER_VERSION
RUN go install golang.org/x/tools/cmd/stringer@${STRINGER_VERSION} \
&& mv /go/bin/stringer /toolchain/go/bin/stringer
ARG DEEPCOPY_GEN_VERSION
RUN go install k8s.io/code-generator/cmd/deepcopy-gen@${DEEPCOPY_GEN_VERSION} \
&& mv /go/bin/deepcopy-gen /toolchain/go/bin/deepcopy-gen
RUN curl -sfL https://github.com/uber/prototool/releases/download/v1.10.0/prototool-Linux-x86_64.tar.gz | tar -xz --strip-components=2 -C /toolchain/bin prototool/bin/prototool
COPY ./hack/docgen /go/src/github.com/talos-systems/talos-hack-docgen
RUN cd /go/src/github.com/talos-systems/talos-hack-docgen \
@ -165,6 +168,7 @@ RUN gofumports -w -local github.com/talos-systems/talos /api/
# run docgen for machinery config
FROM build-go AS go-generate
COPY ./pkg ./pkg
COPY ./hack/boilerplate.txt ./hack/boilerplate.txt
RUN --mount=type=cache,target=/.cache go generate ./pkg/...
WORKDIR /src/pkg/machinery
RUN --mount=type=cache,target=/.cache go generate ./...

View File

@ -16,6 +16,7 @@ EXTRAS ?= v0.4.0
GO_VERSION ?= 1.16
GOFUMPT_VERSION ?= v0.1.0
STRINGER_VERSION ?= v0.1.3
DEEPCOPY_GEN_VERSION ?= v0.21.2
IMPORTVET ?= autonomy/importvet:f6b07d9
OPERATING_SYSTEM := $(shell uname -s | tr "[:upper:]" "[:lower:]")
TALOSCTL_DEFAULT_TARGET := talosctl-$(OPERATING_SYSTEM)
@ -79,6 +80,7 @@ COMMON_ARGS += --build-arg=PKGS=$(PKGS)
COMMON_ARGS += --build-arg=EXTRAS=$(EXTRAS)
COMMON_ARGS += --build-arg=GOFUMPT_VERSION=$(GOFUMPT_VERSION)
COMMON_ARGS += --build-arg=STRINGER_VERSION=$(STRINGER_VERSION)
COMMON_ARGS += --build-arg=DEEPCOPY_GEN_VERSION=$(DEEPCOPY_GEN_VERSION)
COMMON_ARGS += --build-arg=TAG=$(TAG)
COMMON_ARGS += --build-arg=ARTIFACTS=$(ARTIFACTS)
COMMON_ARGS += --build-arg=IMPORTVET=$(IMPORTVET)

2
go.mod
View File

@ -71,7 +71,7 @@ require (
github.com/smira/go-xz v0.0.0-20201019130106-9921ed7a9935
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
github.com/talos-systems/crypto v0.3.1
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e
github.com/talos-systems/go-blockdevice v0.2.1
github.com/talos-systems/go-cmd v0.1.0
github.com/talos-systems/go-debug v0.2.1

4
go.sum
View File

@ -1138,8 +1138,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/talos-systems/crypto v0.3.1 h1:TSUfwrN3+sAunR/e78pU8JMbBo9VDfS3fJtpMg7xQ6k=
github.com/talos-systems/crypto v0.3.1/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e h1:7mNVNvTTRA7mqflb/34iSJrimISfRErruMyptRAGWkg=
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/go-blockdevice v0.2.1 h1:swoY5NcssuMgdCf/dlMngNDgEAasGp2jviPqAz9Epss=
github.com/talos-systems/go-blockdevice v0.2.1/go.mod h1:qnn/zDc09I1DA2BUDDCOSA2D0P8pIDjN8pGiRoRaQig=
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0/go.mod h1:kf+rZzTEmlDiYQ6ulslvRONnKLQH8x83TowltGMhO+k=

3
hack/boilerplate.txt Normal file
View File

@ -0,0 +1,3 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -0,0 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// +k8s:deepcopy-gen=package
package v1alpha1

View File

@ -22,6 +22,7 @@ import (
// ConfigBundle defines the group of v1alpha1 config files.
// docgen: nodoc
// +k8s:deepcopy-gen=false
type ConfigBundle struct {
InitCfg *Config
ControlPlaneCfg *Config

View File

@ -304,7 +304,17 @@ func (k *KubeletConfig) ExtraArgs() map[string]string {
// ExtraMounts implements the config.Provider interface.
func (k *KubeletConfig) ExtraMounts() []specs.Mount {
return k.KubeletExtraMounts
if k.KubeletExtraMounts == nil {
return nil
}
out := make([]specs.Mount, len(k.KubeletExtraMounts))
for i := range k.KubeletExtraMounts {
out[i] = k.KubeletExtraMounts[i].Mount
}
return out
}
// RegisterWithFQDN implements the config.Provider interface.

View File

@ -6,9 +6,8 @@
Package v1alpha1 configuration file contains all the options available for configuring a machine.
To generate a set of basic configuration files, run:
```bash
talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>
````
talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>
This will generate a machine config for each node type, and a talosconfig for the CLI.
*/
@ -16,6 +15,8 @@ package v1alpha1
//go:generate docgen ./v1alpha1_types.go ./v1alpha1_types_doc.go Configuration
//go:generate deepcopy-gen --input-dirs ../v1alpha1/ --go-header-file ../../../../../hack/boilerplate.txt --bounding-dirs ../v1alpha1 -O zz_generated.deepcopy
import (
"fmt"
"net/url"
@ -752,6 +753,28 @@ type ClusterConfig struct {
AllowSchedulingOnMasters bool `yaml:"allowSchedulingOnMasters,omitempty"`
}
// ExtraMount wraps OCI Mount specification.
type ExtraMount struct {
specs.Mount
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExtraMount) DeepCopyInto(out *ExtraMount) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMount.
func (in *ExtraMount) DeepCopy() *ExtraMount {
if in == nil {
return nil
}
out := new(ExtraMount)
in.DeepCopyInto(out)
return out
}
// KubeletConfig represents the kubelet config values.
type KubeletConfig struct {
// description: |
@ -771,7 +794,7 @@ type KubeletConfig struct {
// The `extraMounts` field is used to add additional mounts to the kubelet container.
// examples:
// - value: kubeletExtraMountsExample
KubeletExtraMounts []specs.Mount `yaml:"extraMounts,omitempty"`
KubeletExtraMounts []ExtraMount `yaml:"extraMounts,omitempty"`
// description: |
// The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.
// This is required in clouds like AWS.
@ -865,6 +888,11 @@ type InstallDiskSizeMatcher struct {
condition string
}
// DeepCopyInto implements DeepCopy interface.
func (m *InstallDiskSizeMatcher) DeepCopyInto(out *InstallDiskSizeMatcher) {
*out = *m
}
// MarshalYAML is a custom marshaller for `InstallDiskSizeMatcher`.
func (m *InstallDiskSizeMatcher) MarshalYAML() (interface{}, error) {
return m.condition, nil
@ -1067,6 +1095,29 @@ func (e *Endpoint) MarshalYAML() (interface{}, error) {
return e.URL.String(), nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (e *Endpoint) DeepCopyInto(out *Endpoint) {
*out = *e
if e.URL != nil {
in, out := &e.URL, &out.URL
*out = new(url.URL)
*out = *in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint.
func (e *Endpoint) DeepCopy() *Endpoint {
if e == nil {
return nil
}
out := new(Endpoint)
e.DeepCopyInto(out)
return out
}
// ControlPlaneConfig represents the control plane configuration options.
type ControlPlaneConfig struct {
// description: |

View File

@ -14,6 +14,7 @@ var (
ConfigDoc encoder.Doc
MachineConfigDoc encoder.Doc
ClusterConfigDoc encoder.Doc
ExtraMountDoc encoder.Doc
KubeletConfigDoc encoder.Doc
NetworkConfigDoc encoder.Doc
InstallConfigDoc encoder.Doc
@ -401,6 +402,19 @@ func init() {
"no",
}
ExtraMountDoc.Type = "ExtraMount"
ExtraMountDoc.Comments[encoder.LineComment] = "ExtraMount wraps OCI Mount specification."
ExtraMountDoc.Description = "ExtraMount wraps OCI Mount specification."
ExtraMountDoc.AddExample("", kubeletExtraMountsExample)
ExtraMountDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "KubeletConfig",
FieldName: "extraMounts",
},
}
ExtraMountDoc.Fields = make([]encoder.Doc, 0)
KubeletConfigDoc.Type = "KubeletConfig"
KubeletConfigDoc.Comments[encoder.LineComment] = "KubeletConfig represents the kubelet config values."
KubeletConfigDoc.Description = "KubeletConfig represents the kubelet config values."
@ -430,7 +444,7 @@ func init() {
"key": "value",
})
KubeletConfigDoc.Fields[2].Name = "extraMounts"
KubeletConfigDoc.Fields[2].Type = "[]Mount"
KubeletConfigDoc.Fields[2].Type = "[]ExtraMount"
KubeletConfigDoc.Fields[2].Note = ""
KubeletConfigDoc.Fields[2].Description = "The `extraMounts` field is used to add additional mounts to the kubelet container."
KubeletConfigDoc.Fields[2].Comments[encoder.LineComment] = "The `extraMounts` field is used to add additional mounts to the kubelet container."
@ -1882,6 +1896,10 @@ func (_ ClusterConfig) Doc() *encoder.Doc {
return &ClusterConfigDoc
}
func (_ ExtraMount) Doc() *encoder.Doc {
return &ExtraMountDoc
}
func (_ KubeletConfig) Doc() *encoder.Doc {
return &KubeletConfigDoc
}
@ -2061,12 +2079,13 @@ func (_ ClusterInlineManifest) Doc() *encoder.Doc {
// GetConfigurationDoc returns documentation for the file ./v1alpha1_types_doc.go.
func GetConfigurationDoc() *encoder.FileDoc {
return &encoder.FileDoc{
Name: "Configuration",
Description: "Package v1alpha1 configuration file contains all the options available for configuring a machine.\n\nTo generate a set of basic configuration files, run:\n```bash\ntalosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>\n````\n\nThis will generate a machine config for each node type, and a talosconfig for the CLI.\n",
Name: "Configuration",
Description: "Package v1alpha1 configuration file contains all the options available for configuring a machine.\n\nTo generate a set of basic configuration files, run:\n\n talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>\n\nThis will generate a machine config for each node type, and a talosconfig for the CLI.\n",
Structs: []*encoder.Doc{
&ConfigDoc,
&MachineConfigDoc,
&ClusterConfigDoc,
&ExtraMountDoc,
&KubeletConfigDoc,
&NetworkConfigDoc,
&InstallConfigDoc,

View File

@ -2,7 +2,6 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package v1alpha1 provides user-facing v1alpha1 machine configs
package v1alpha1
import (

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ require (
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d
github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0
github.com/talos-systems/crypto v0.3.1
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e
github.com/talos-systems/go-blockdevice v0.2.1
github.com/talos-systems/net v0.3.0
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c

View File

@ -154,8 +154,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/talos-systems/crypto v0.3.1 h1:TSUfwrN3+sAunR/e78pU8JMbBo9VDfS3fJtpMg7xQ6k=
github.com/talos-systems/crypto v0.3.1/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e h1:7mNVNvTTRA7mqflb/34iSJrimISfRErruMyptRAGWkg=
github.com/talos-systems/crypto v0.3.2-0.20210707205149-deec8d47700e/go.mod h1:xaNCB2/Bxaj+qrkdeodhRv5eKQVvKOGBBMj58MrIPY8=
github.com/talos-systems/go-blockdevice v0.2.1 h1:swoY5NcssuMgdCf/dlMngNDgEAasGp2jviPqAz9Epss=
github.com/talos-systems/go-blockdevice v0.2.1/go.mod h1:qnn/zDc09I1DA2BUDDCOSA2D0P8pIDjN8pGiRoRaQig=
github.com/talos-systems/go-cmd v0.0.0-20210216164758-68eb0067e0f0/go.mod h1:kf+rZzTEmlDiYQ6ulslvRONnKLQH8x83TowltGMhO+k=

View File

@ -11,7 +11,6 @@ import (
"github.com/cosi-project/runtime/pkg/resource/meta"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/configloader"
"github.com/talos-systems/talos/pkg/machinery/config/encoder"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
)
@ -66,20 +65,10 @@ func (r *MachineConfig) String() string {
// DeepCopy implements resource.Resource.
func (r *MachineConfig) DeepCopy() resource.Resource {
b, err := r.spec.cfg.Bytes()
if err != nil {
panic(err) // TODO: DeepCopy() should support returning errors? or config should implement DeeCopy without errors?
}
c, err := configloader.NewFromBytes(b)
if err != nil {
panic(err)
}
return &MachineConfig{
md: r.md,
spec: &v1alpha1Spec{
cfg: c.(*v1alpha1.Config),
cfg: r.spec.cfg.(*v1alpha1.Config).DeepCopy(),
},
}
}

View File

@ -11,9 +11,8 @@ desription: Talos node configuration file reference.
Package v1alpha1 configuration file contains all the options available for configuring a machine.
To generate a set of basic configuration files, run:
```bash
talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>
````
talosctl gen config --version v1alpha1 <cluster name> <cluster endpoint>
This will generate a machine config for each node type, and a talosconfig for the CLI.
@ -1287,6 +1286,26 @@ Valid values:
## ExtraMount
ExtraMount wraps OCI Mount specification.
Appears in:
- <code><a href="#kubeletconfig">KubeletConfig</a>.extraMounts</code>
``` yaml
- destination: /var/lib/example
type: bind
source: /var/lib/example
options:
- rshared
- rw
```
## KubeletConfig
KubeletConfig represents the kubelet config values.
@ -1363,7 +1382,7 @@ extraArgs:
<div class="dd">
<code>extraMounts</code> <i>[]Mount</i>
<code>extraMounts</code> <i>[]<a href="#extramount">ExtraMount</a></i>
</div>
<div class="dt">