mirror of
https://github.com/siderolabs/talos.git
synced 2025-09-29 17:51:13 +02:00
test: fix the integrtion tests for apply-config
They got broken after refactoring. Also use this PR to test things before the release. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
parent
076f3c4f20
commit
2512ef435f
24
Dockerfile
24
Dockerfile
@ -504,9 +504,9 @@ FROM scratch AS talosctl
|
|||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
COPY --from=talosctl-all /talosctl-linux-${TARGETARCH} /talosctl
|
COPY --from=talosctl-all /talosctl-linux-${TARGETARCH} /talosctl
|
||||||
ARG TAG
|
ARG TAG
|
||||||
ENV VERSION ${TAG}
|
ENV VERSION=${TAG}
|
||||||
LABEL "alpha.talos.dev/version"="${VERSION}"
|
LABEL "alpha.talos.dev/version"="${VERSION}"
|
||||||
LABEL org.opencontainers.image.source https://github.com/siderolabs/talos
|
LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos
|
||||||
ENTRYPOINT ["/talosctl"]
|
ENTRYPOINT ["/talosctl"]
|
||||||
|
|
||||||
# The kernel target is the linux kernel.
|
# The kernel target is the linux kernel.
|
||||||
@ -755,7 +755,7 @@ COPY --from=initramfs-archive /initramfs.xz /initramfs-${TARGETARCH}.xz
|
|||||||
|
|
||||||
FROM scratch AS talos
|
FROM scratch AS talos
|
||||||
COPY --from=rootfs / /
|
COPY --from=rootfs / /
|
||||||
LABEL org.opencontainers.image.source https://github.com/siderolabs/talos
|
LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos
|
||||||
ENTRYPOINT ["/sbin/init"]
|
ENTRYPOINT ["/sbin/init"]
|
||||||
|
|
||||||
# The installer target generates an image that can be used to install Talos to
|
# The installer target generates an image that can be used to install Talos to
|
||||||
@ -793,7 +793,7 @@ FROM install-artifacts-${INSTALLER_ARCH} AS install-artifacts
|
|||||||
|
|
||||||
FROM alpine:3.18.4 AS installer-image
|
FROM alpine:3.18.4 AS installer-image
|
||||||
ARG SOURCE_DATE_EPOCH
|
ARG SOURCE_DATE_EPOCH
|
||||||
ENV SOURCE_DATE_EPOCH ${SOURCE_DATE_EPOCH}
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
RUN apk add --no-cache --update --no-scripts \
|
RUN apk add --no-cache --update --no-scripts \
|
||||||
bash \
|
bash \
|
||||||
binutils-aarch64 \
|
binutils-aarch64 \
|
||||||
@ -813,7 +813,7 @@ RUN apk add --no-cache --update --no-scripts \
|
|||||||
xz \
|
xz \
|
||||||
zstd
|
zstd
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ENV TARGETARCH ${TARGETARCH}
|
ENV TARGETARCH=${TARGETARCH}
|
||||||
COPY --from=installer-build /installer /bin/installer
|
COPY --from=installer-build /installer /bin/installer
|
||||||
COPY --chmod=0644 hack/extra-modules.conf /etc/modules.d/10-extra-modules.conf
|
COPY --chmod=0644 hack/extra-modules.conf /etc/modules.d/10-extra-modules.conf
|
||||||
COPY --from=pkg-grub / /
|
COPY --from=pkg-grub / /
|
||||||
@ -827,9 +827,9 @@ RUN find /bin /etc /lib /usr /sbin | grep -Ev '/etc/hosts|/etc/resolv.conf' \
|
|||||||
FROM scratch AS installer-image-squashed
|
FROM scratch AS installer-image-squashed
|
||||||
COPY --from=installer-image / /
|
COPY --from=installer-image / /
|
||||||
ARG TAG
|
ARG TAG
|
||||||
ENV VERSION ${TAG}
|
ENV VERSION=${TAG}
|
||||||
LABEL "alpha.talos.dev/version"="${VERSION}"
|
LABEL "alpha.talos.dev/version"="${VERSION}"
|
||||||
LABEL org.opencontainers.image.source https://github.com/siderolabs/talos
|
LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos
|
||||||
ENTRYPOINT ["/bin/installer"]
|
ENTRYPOINT ["/bin/installer"]
|
||||||
|
|
||||||
FROM installer-image-squashed AS installer
|
FROM installer-image-squashed AS installer
|
||||||
@ -841,7 +841,7 @@ ENTRYPOINT ["/bin/imager"]
|
|||||||
|
|
||||||
FROM imager AS iso-amd64-build
|
FROM imager AS iso-amd64-build
|
||||||
ARG SOURCE_DATE_EPOCH
|
ARG SOURCE_DATE_EPOCH
|
||||||
ENV SOURCE_DATE_EPOCH ${SOURCE_DATE_EPOCH}
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
RUN /bin/installer \
|
RUN /bin/installer \
|
||||||
iso \
|
iso \
|
||||||
--arch amd64 \
|
--arch amd64 \
|
||||||
@ -849,7 +849,7 @@ RUN /bin/installer \
|
|||||||
|
|
||||||
FROM imager AS iso-arm64-build
|
FROM imager AS iso-arm64-build
|
||||||
ARG SOURCE_DATE_EPOCH
|
ARG SOURCE_DATE_EPOCH
|
||||||
ENV SOURCE_DATE_EPOCH ${SOURCE_DATE_EPOCH}
|
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
|
||||||
RUN /bin/installer \
|
RUN /bin/installer \
|
||||||
iso \
|
iso \
|
||||||
--arch arm64 \
|
--arch arm64 \
|
||||||
@ -868,7 +868,7 @@ FROM base AS unit-tests-runner
|
|||||||
RUN unlink /etc/ssl
|
RUN unlink /etc/ssl
|
||||||
COPY --from=rootfs / /
|
COPY --from=rootfs / /
|
||||||
ARG TESTPKGS
|
ARG TESTPKGS
|
||||||
ENV PLATFORM container
|
ENV PLATFORM=container
|
||||||
ARG GO_LDFLAGS
|
ARG GO_LDFLAGS
|
||||||
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -failfast -v \
|
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -failfast -v \
|
||||||
-ldflags "${GO_LDFLAGS}" \
|
-ldflags "${GO_LDFLAGS}" \
|
||||||
@ -882,8 +882,8 @@ FROM base AS unit-tests-race
|
|||||||
RUN unlink /etc/ssl
|
RUN unlink /etc/ssl
|
||||||
COPY --from=rootfs / /
|
COPY --from=rootfs / /
|
||||||
ARG TESTPKGS
|
ARG TESTPKGS
|
||||||
ENV PLATFORM container
|
ENV PLATFORM=container
|
||||||
ENV CGO_ENABLED 1
|
ENV CGO_ENABLED=1
|
||||||
ARG GO_LDFLAGS
|
ARG GO_LDFLAGS
|
||||||
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -v \
|
RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache go test -v \
|
||||||
-ldflags "${GO_LDFLAGS}" \
|
-ldflags "${GO_LDFLAGS}" \
|
||||||
|
2
go.mod
2
go.mod
@ -77,7 +77,6 @@ require (
|
|||||||
github.com/gizak/termui/v3 v3.1.0
|
github.com/gizak/termui/v3 v3.1.0
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/go-cmp v0.6.0
|
|
||||||
github.com/google/go-containerregistry v0.19.2
|
github.com/google/go-containerregistry v0.19.2
|
||||||
github.com/google/go-tpm v0.9.1
|
github.com/google/go-tpm v0.9.1
|
||||||
github.com/google/nftables v0.2.0
|
github.com/google/nftables v0.2.0
|
||||||
@ -249,6 +248,7 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/gorilla/websocket v1.5.1 // indirect
|
github.com/gorilla/websocket v1.5.1 // indirect
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
stdcmp "cmp"
|
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -28,7 +27,6 @@ import (
|
|||||||
"github.com/cosi-project/runtime/pkg/safe"
|
"github.com/cosi-project/runtime/pkg/safe"
|
||||||
"github.com/cosi-project/runtime/pkg/state"
|
"github.com/cosi-project/runtime/pkg/state"
|
||||||
"github.com/cosi-project/runtime/pkg/state/protobuf/server"
|
"github.com/cosi-project/runtime/pkg/state/protobuf/server"
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gopacket/gopacket/afpacket"
|
"github.com/gopacket/gopacket/afpacket"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
@ -78,11 +76,10 @@ import (
|
|||||||
timeapi "github.com/siderolabs/talos/pkg/machinery/api/time"
|
timeapi "github.com/siderolabs/talos/pkg/machinery/api/time"
|
||||||
clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config"
|
clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config"
|
"github.com/siderolabs/talos/pkg/machinery/config"
|
||||||
docscfg "github.com/siderolabs/talos/pkg/machinery/config/config"
|
"github.com/siderolabs/talos/pkg/machinery/config/configdiff"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
|
"github.com/siderolabs/talos/pkg/machinery/config/configloader"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
|
"github.com/siderolabs/talos/pkg/machinery/config/generate/secrets"
|
||||||
machinetype "github.com/siderolabs/talos/pkg/machinery/config/machine"
|
machinetype "github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
|
||||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||||
etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd"
|
etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd"
|
||||||
@ -221,7 +218,10 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
if in.DryRun {
|
if in.DryRun {
|
||||||
details := generateDiff(s.Controller.Runtime(), cfgProvider)
|
details, err := generateDiff(s.Controller.Runtime(), cfgProvider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate diff: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return &machine.ApplyConfigurationResponse{
|
return &machine.ApplyConfigurationResponse{
|
||||||
Messages: []*machine.ApplyConfiguration{
|
Messages: []*machine.ApplyConfiguration{
|
||||||
@ -295,33 +295,17 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDiff(r runtime.Runtime, provider config.Provider) string {
|
func generateDiff(r runtime.Runtime, provider config.Provider) (string, error) {
|
||||||
var cfg *v1alpha1.Config
|
documentsDiff, err := configdiff.DiffToString(r.ConfigContainer(), provider)
|
||||||
|
if err != nil {
|
||||||
if r.Config() != nil {
|
return "", err
|
||||||
cfg = r.ConfigContainer().RawV1Alpha1()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v1alpha1Diff := cmp.Diff(cfg, provider.RawV1Alpha1(), cmp.AllowUnexported(v1alpha1.InstallDiskSizeMatcher{}))
|
|
||||||
if v1alpha1Diff == "" {
|
|
||||||
v1alpha1Diff = "No changes."
|
|
||||||
}
|
|
||||||
|
|
||||||
origDocs := slices.DeleteFunc(r.ConfigContainer().Documents(), func(doc docscfg.Document) bool { return doc.Kind() == v1alpha1.Version })
|
|
||||||
newDocs := slices.DeleteFunc(provider.Documents(), func(doc docscfg.Document) bool { return doc.Kind() == v1alpha1.Version })
|
|
||||||
|
|
||||||
slices.SortStableFunc(origDocs, func(a, b docscfg.Document) int { return stdcmp.Compare(a.Kind(), b.Kind()) })
|
|
||||||
slices.SortStableFunc(newDocs, func(a, b docscfg.Document) int { return stdcmp.Compare(a.Kind(), b.Kind()) })
|
|
||||||
|
|
||||||
documentsDiff := cmp.Diff(origDocs, newDocs)
|
|
||||||
if documentsDiff == "" {
|
if documentsDiff == "" {
|
||||||
documentsDiff = "No changes."
|
documentsDiff = "No changes."
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(`Config diff:
|
return "Config diff:\n\n" + documentsDiff, nil
|
||||||
%s
|
|
||||||
Documents diff:
|
|
||||||
%s`, v1alpha1Diff, documentsDiff)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateConfiguration implements the machine.MachineServer interface.
|
// GenerateConfiguration implements the machine.MachineServer interface.
|
||||||
|
@ -16,13 +16,13 @@ import (
|
|||||||
|
|
||||||
"github.com/cosi-project/runtime/pkg/resource"
|
"github.com/cosi-project/runtime/pkg/resource"
|
||||||
"github.com/cosi-project/runtime/pkg/safe"
|
"github.com/cosi-project/runtime/pkg/safe"
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
|
|
||||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system"
|
"github.com/siderolabs/talos/internal/app/machined/pkg/system"
|
||||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system/services"
|
"github.com/siderolabs/talos/internal/app/machined/pkg/system/services"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config"
|
"github.com/siderolabs/talos/pkg/machinery/config"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
"github.com/siderolabs/talos/pkg/machinery/config/configdiff"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
||||||
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
|
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
|
||||||
)
|
)
|
||||||
@ -165,9 +165,12 @@ func (r *Runtime) CanApplyImmediate(cfg config.Provider) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(currentConfig, newConfig) {
|
if !reflect.DeepEqual(currentConfig, newConfig) {
|
||||||
diff := cmp.Diff(currentConfig, newConfig, cmp.AllowUnexported(v1alpha1.InstallDiskSizeMatcher{}))
|
diff, err := configdiff.DiffToString(container.NewV1Alpha1(currentConfig), container.NewV1Alpha1(newConfig))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error calculating diff: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("this config change can't be applied in immediate mode\ndiff: %s", diff)
|
return fmt.Errorf("this config change can't be applied in immediate mode\ndiff:\n%s", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -98,7 +98,7 @@ func (suite *ApplyConfigSuite) TestApply() {
|
|||||||
nodeCtx := client.WithNode(suite.ctx, node)
|
nodeCtx := client.WithNode(suite.ctx, node)
|
||||||
|
|
||||||
provider, err := suite.ReadConfigFromNode(nodeCtx)
|
provider, err := suite.ReadConfigFromNode(nodeCtx)
|
||||||
suite.Assert().NoErrorf(err, "failed to read existing config from node %q: %w", node, err)
|
suite.Assert().NoErrorf(err, "failed to read existing config from node %q", node)
|
||||||
|
|
||||||
cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) {
|
cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) {
|
||||||
if cfg.MachineConfig.MachineSysctls == nil {
|
if cfg.MachineConfig.MachineSysctls == nil {
|
||||||
@ -118,7 +118,7 @@ func (suite *ApplyConfigSuite) TestApply() {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// It is expected that the connection will EOF here, so just log the error
|
// It is expected that the connection will EOF here, so just log the error
|
||||||
suite.Assert().NoErrorf(err, "failed to apply configuration (node %q): %w", node, err)
|
suite.Assert().NoErrorf(err, "failed to apply configuration (node %q)", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -138,7 +138,7 @@ func (suite *ApplyConfigSuite) TestApply() {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
), "failed to read updated configuration from node %q: %w", node, err,
|
), "failed to read updated configuration from node %q", node,
|
||||||
)
|
)
|
||||||
|
|
||||||
suite.Assert().Equal(
|
suite.Assert().Equal(
|
||||||
@ -177,14 +177,14 @@ func (suite *ApplyConfigSuite) TestApplyWithoutReboot() {
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
suite.Require().NoError(err, "failed to apply deferred configuration (node %q)", node)
|
||||||
|
|
||||||
// Verify configuration change
|
// Verify configuration change
|
||||||
var newProvider config.Provider
|
var newProvider config.Provider
|
||||||
|
|
||||||
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
||||||
|
|
||||||
suite.Require().NoError(err, "failed to read updated configuration from node %q: %w", node)
|
suite.Require().NoError(err, "failed to read updated configuration from node %q", node)
|
||||||
|
|
||||||
if mode == machineapi.ApplyConfigurationRequest_AUTO {
|
if mode == machineapi.ApplyConfigurationRequest_AUTO {
|
||||||
suite.Assert().Equal(
|
suite.Assert().Equal(
|
||||||
@ -206,7 +206,7 @@ func (suite *ApplyConfigSuite) TestApplyWithoutReboot() {
|
|||||||
Mode: mode,
|
Mode: mode,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
|
suite.Require().NoError(err, "failed to apply deferred configuration (node %q)", node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +303,7 @@ func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// It is expected that the connection will EOF here, so just log the error
|
// It is expected that the connection will EOF here, so just log the error
|
||||||
suite.Assert().Errorf(err, "failed to apply configuration (node %q): %w", node, err)
|
suite.T().Logf("failed to apply configuration (node %q): %s", node, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -315,7 +315,7 @@ func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
|
|||||||
// Verify configuration change
|
// Verify configuration change
|
||||||
var newProvider config.Provider
|
var newProvider config.Provider
|
||||||
|
|
||||||
suite.Require().Errorf(
|
suite.Require().NoError(
|
||||||
retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry(
|
retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry(
|
||||||
func() error {
|
func() error {
|
||||||
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
newProvider, err = suite.ReadConfigFromNode(nodeCtx)
|
||||||
@ -325,7 +325,7 @@ func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
), "failed to read updated configuration from node %q: %w", node, err,
|
), "failed to read updated configuration from node %q", node,
|
||||||
)
|
)
|
||||||
|
|
||||||
e := newProvider.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel)
|
e := newProvider.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel)
|
||||||
@ -504,10 +504,10 @@ func (suite *ApplyConfigSuite) TestApplyTry() {
|
|||||||
TryModeTimeout: durationpb.New(time.Second * 1),
|
TryModeTimeout: durationpb.New(time.Second * 1),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
suite.Assert().NoErrorf(err, "failed to apply configuration (node %q): %s", node, err)
|
suite.Assert().NoErrorf(err, "failed to apply configuration (node %q)", node)
|
||||||
|
|
||||||
provider, err = getMachineConfig(nodeCtx)
|
provider, err = getMachineConfig(nodeCtx)
|
||||||
suite.Require().NoErrorf(err, "failed to read existing config from node %q: %w", node, err)
|
suite.Require().NoErrorf(err, "failed to read existing config from node %q", node)
|
||||||
|
|
||||||
suite.Assert().NotNil(provider.Config().Machine().Network())
|
suite.Assert().NotNil(provider.Config().Machine().Network())
|
||||||
suite.Assert().NotNil(provider.Config().Machine().Network().Devices())
|
suite.Assert().NotNil(provider.Config().Machine().Network().Devices())
|
||||||
|
129
pkg/machinery/config/configdiff/configdiff.go
Normal file
129
pkg/machinery/config/configdiff/configdiff.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// 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/.
|
||||||
|
|
||||||
|
// Package configdiff provides a way to compare two config trees.
|
||||||
|
package configdiff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/hexops/gotextdiff"
|
||||||
|
"github.com/hexops/gotextdiff/myers"
|
||||||
|
"github.com/hexops/gotextdiff/span"
|
||||||
|
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Diff outputs (optionally) colorized diff between two machine configurations.
|
||||||
|
//
|
||||||
|
// One of the resources might be nil.
|
||||||
|
func Diff(w io.Writer, oldCfg, newCfg config.Encoder) error {
|
||||||
|
var (
|
||||||
|
oldYaml, newYaml []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if oldCfg != nil {
|
||||||
|
oldYaml, err = oldCfg.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if newCfg != nil {
|
||||||
|
newYaml, err = newCfg.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edits := myers.ComputeEdits(span.URIFromPath("a"), string(oldYaml), string(newYaml))
|
||||||
|
diff := gotextdiff.ToUnified("a", "b", string(oldYaml), edits)
|
||||||
|
|
||||||
|
outputDiff(w, diff, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffToString returns a string representation of the diff between two machine configurations.
|
||||||
|
func DiffToString(oldCfg, newCfg config.Encoder) (string, error) {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
err := Diff(&sb, oldCfg, newCfg)
|
||||||
|
|
||||||
|
return sb.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func outputDiff(w io.Writer, u gotextdiff.Unified, noColor bool) {
|
||||||
|
if len(u.Hunks) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bold := color.New(color.Bold)
|
||||||
|
cyan := color.New(color.FgCyan)
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
green := color.New(color.FgGreen)
|
||||||
|
|
||||||
|
if noColor {
|
||||||
|
bold.DisableColor()
|
||||||
|
cyan.DisableColor()
|
||||||
|
red.DisableColor()
|
||||||
|
green.DisableColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
bold.Fprintf(w, "--- %s\n", u.From) //nolint:errcheck
|
||||||
|
bold.Fprintf(w, "+++ %s\n", u.To) //nolint:errcheck
|
||||||
|
|
||||||
|
for _, hunk := range u.Hunks {
|
||||||
|
fromCount, toCount := 0, 0
|
||||||
|
|
||||||
|
for _, l := range hunk.Lines {
|
||||||
|
switch l.Kind { //nolint:exhaustive
|
||||||
|
case gotextdiff.Delete:
|
||||||
|
fromCount++
|
||||||
|
case gotextdiff.Insert:
|
||||||
|
toCount++
|
||||||
|
default:
|
||||||
|
fromCount++
|
||||||
|
toCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cyan.Fprintf(w, "@@") //nolint:errcheck
|
||||||
|
|
||||||
|
if fromCount > 1 {
|
||||||
|
cyan.Fprintf(w, " -%d,%d", hunk.FromLine, fromCount) //nolint:errcheck
|
||||||
|
} else {
|
||||||
|
cyan.Fprintf(w, " -%d", hunk.FromLine) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
if toCount > 1 {
|
||||||
|
cyan.Fprintf(w, " +%d,%d", hunk.ToLine, toCount) //nolint:errcheck
|
||||||
|
} else {
|
||||||
|
cyan.Fprintf(w, " +%d", hunk.ToLine) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
cyan.Printf(" @@\n") //nolint:errcheck
|
||||||
|
|
||||||
|
for _, l := range hunk.Lines {
|
||||||
|
switch l.Kind { //nolint:exhaustive
|
||||||
|
case gotextdiff.Delete:
|
||||||
|
red.Fprintf(w, "-%s", l.Content) //nolint:errcheck
|
||||||
|
case gotextdiff.Insert:
|
||||||
|
green.Fprintf(w, "+%s", l.Content) //nolint:errcheck
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(w, " %s", l.Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(l.Content, "\n") {
|
||||||
|
red.Fprintf(w, "\n\\ No newline at end of file\n") //nolint:errcheck
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
pkg/machinery/config/configdiff/configdiff_test.go
Normal file
84
pkg/machinery/config/configdiff/configdiff_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// 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/.
|
||||||
|
|
||||||
|
package configdiff_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/siderolabs/gen/xtesting/must"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/configdiff"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/types/meta"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/types/siderolink"
|
||||||
|
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiffString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
v1alpha1Cfg := &v1alpha1.Config{
|
||||||
|
ConfigVersion: "v1alpha1",
|
||||||
|
MachineConfig: &v1alpha1.MachineConfig{
|
||||||
|
MachineType: "controlplane",
|
||||||
|
MachineToken: "foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
v1alpha1CfgOther := v1alpha1Cfg.DeepCopy()
|
||||||
|
v1alpha1CfgOther.MachineConfig.MachineType = "worker"
|
||||||
|
|
||||||
|
siderolinkConfig := siderolink.NewConfigV1Alpha1()
|
||||||
|
siderolinkConfig.APIUrlConfig = meta.URL{
|
||||||
|
URL: must.Value(url.Parse("https://example.com"))(t),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
oldCfg []config.Document
|
||||||
|
newCfg []config.Document
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
oldCfg: nil,
|
||||||
|
newCfg: nil,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same",
|
||||||
|
oldCfg: []config.Document{v1alpha1Cfg},
|
||||||
|
newCfg: []config.Document{v1alpha1Cfg},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new doc",
|
||||||
|
oldCfg: []config.Document{v1alpha1Cfg},
|
||||||
|
newCfg: []config.Document{v1alpha1Cfg, siderolinkConfig},
|
||||||
|
want: "--- a\n+++ b\n@@ -4,3 +4,7 token: foo\n certSANs: []\n cluster: null\n+---\n+apiVersion: v1alpha1\n+kind: SideroLinkConfig\n+apiUrl: https://example.com\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "updated field",
|
||||||
|
oldCfg: []config.Document{v1alpha1Cfg, siderolinkConfig},
|
||||||
|
newCfg: []config.Document{v1alpha1CfgOther, siderolinkConfig},
|
||||||
|
want: "--- a\n+++ b\n@@ -1,6 +1,6 version: v1alpha1\n machine:\n- type: controlplane\n+ type: worker\n token: foo\n certSANs: []\n cluster: null\n",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
oldCfg := must.Value(container.New(test.oldCfg...))(t)
|
||||||
|
newCfg := must.Value(container.New(test.newCfg...))(t)
|
||||||
|
|
||||||
|
got, err := configdiff.DiffToString(oldCfg, newCfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, test.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,10 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/emicklei/dot v1.6.2
|
github.com/emicklei/dot v1.6.2
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible
|
github.com/evanphx/json-patch v5.9.0+incompatible
|
||||||
|
github.com/fatih/color v1.17.0
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
|
github.com/hexops/gotextdiff v1.0.3
|
||||||
github.com/jsimonetti/rtnetlink/v2 v2.0.2
|
github.com/jsimonetti/rtnetlink/v2 v2.0.2
|
||||||
github.com/mdlayher/ethtool v0.1.0
|
github.com/mdlayher/ethtool v0.1.0
|
||||||
github.com/opencontainers/runtime-spec v1.2.0
|
github.com/opencontainers/runtime-spec v1.2.0
|
||||||
@ -47,6 +49,8 @@ require (
|
|||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/josharian/native v1.1.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/mdlayher/socket v0.5.1 // indirect
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
|
@ -33,6 +33,8 @@ github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
|||||||
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||||
|
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||||
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
||||||
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
@ -52,6 +54,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jsimonetti/rtnetlink/v2 v2.0.2 h1:ZKlbCujrIpp4/u3V2Ka0oxlf4BCkt6ojkvpy3nZoCBY=
|
github.com/jsimonetti/rtnetlink/v2 v2.0.2 h1:ZKlbCujrIpp4/u3V2Ka0oxlf4BCkt6ojkvpy3nZoCBY=
|
||||||
@ -60,6 +64,11 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mdlayher/ethtool v0.1.0 h1:XAWHsmKhyPOo42qq/yTPb0eFBGUKKTR1rE0dVrWVQ0Y=
|
github.com/mdlayher/ethtool v0.1.0 h1:XAWHsmKhyPOo42qq/yTPb0eFBGUKKTR1rE0dVrWVQ0Y=
|
||||||
github.com/mdlayher/ethtool v0.1.0/go.mod h1:fBMLn2UhfRGtcH5ZFjr+6GUiHEjZsItFD7fSn7jbZVQ=
|
github.com/mdlayher/ethtool v0.1.0/go.mod h1:fBMLn2UhfRGtcH5ZFjr+6GUiHEjZsItFD7fSn7jbZVQ=
|
||||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||||
@ -150,6 +159,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user