cli: use custom byte-slice marshalling for state file (#2460)

* custom byte slice marshalling

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* byte slice compatibility

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* other byte slice compat test

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* add missing dep

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* export byte type alias

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* regenerate exported type

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* test marshal and unmarshal together

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

---------

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
This commit is contained in:
Moritz Sanft 2023-10-17 10:35:54 +02:00 committed by GitHub
parent c424ec8825
commit a8605d7294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 144 additions and 16 deletions

View File

@ -7401,8 +7401,8 @@ def go_dependencies():
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "golang.org/x/net",
sum = "h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=",
version = "v0.17.0",
sum = "h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=",
version = "v0.16.0",
)
go_repository(
name = "org_golang_x_oauth2",

View File

@ -26,5 +26,7 @@ go_test(
"@com_github_siderolabs_talos_pkg_machinery//config/encoder",
"@com_github_spf13_afero//:afero",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@in_gopkg_yaml_v3//:yaml_v3",
],
)

View File

@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package state
import (
"encoding/hex"
"fmt"
"dario.cat/mergo"
@ -63,7 +64,7 @@ type ClusterValues struct {
OwnerID string `yaml:"ownerID"`
// description: |
// Salt used to generate the ClusterID on the bootstrapping node.
MeasurementSalt []byte `yaml:"measurementSalt"`
MeasurementSalt HexBytes `yaml:"measurementSalt"`
}
// Infrastructure describe the state related to the cloud resources of the cluster.
@ -76,7 +77,7 @@ type Infrastructure struct {
ClusterEndpoint string `yaml:"clusterEndpoint"`
// description: |
// Secret used to authenticate the bootstrapping node.
InitSecret []byte `yaml:"initSecret"`
InitSecret HexBytes `yaml:"initSecret"`
// description: |
// List of Subject Alternative Names (SANs) to add to the Kubernetes API server certificate.
// If no SANs should be added, this field can be left empty.
@ -164,3 +165,33 @@ func (s *State) Merge(other *State) (*State, error) {
}
return s, nil
}
// HexBytes is a byte slice that is marshalled to and from a hex string.
type HexBytes []byte
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (h *HexBytes) UnmarshalYAML(unmarshal func(any) error) error {
var hexString string
if err := unmarshal(&hexString); err != nil {
// TODO(msanft): Remove with v2.14.0
// fall back to unmarshalling as a byte slice for backwards compatibility
var oldHexBytes []byte
if err := unmarshal(&oldHexBytes); err != nil {
return fmt.Errorf("unmarshalling hex bytes: %w", err)
}
hexString = hex.EncodeToString(oldHexBytes)
}
bytes, err := hex.DecodeString(hexString)
if err != nil {
return fmt.Errorf("decoding hex bytes: %w", err)
}
*h = bytes
return nil
}
// MarshalYAML implements the yaml.Marshaler interface.
func (h HexBytes) MarshalYAML() (any, error) {
return hex.EncodeToString(h), nil
}

View File

@ -60,7 +60,7 @@ func init() {
ClusterValuesDoc.Fields[1].Description = "Unique identifier of the owner of the cluster."
ClusterValuesDoc.Fields[1].Comments[encoder.LineComment] = "Unique identifier of the owner of the cluster."
ClusterValuesDoc.Fields[2].Name = "measurementSalt"
ClusterValuesDoc.Fields[2].Type = "[]byte"
ClusterValuesDoc.Fields[2].Type = "HexBytes"
ClusterValuesDoc.Fields[2].Note = ""
ClusterValuesDoc.Fields[2].Description = "Salt used to generate the ClusterID on the bootstrapping node."
ClusterValuesDoc.Fields[2].Comments[encoder.LineComment] = "Salt used to generate the ClusterID on the bootstrapping node."
@ -86,7 +86,7 @@ func init() {
InfrastructureDoc.Fields[1].Description = "Endpoint the cluster can be reached at."
InfrastructureDoc.Fields[1].Comments[encoder.LineComment] = "Endpoint the cluster can be reached at."
InfrastructureDoc.Fields[2].Name = "initSecret"
InfrastructureDoc.Fields[2].Type = "[]byte"
InfrastructureDoc.Fields[2].Type = "HexBytes"
InfrastructureDoc.Fields[2].Note = ""
InfrastructureDoc.Fields[2].Description = "Secret used to authenticate the bootstrapping node."
InfrastructureDoc.Fields[2].Comments[encoder.LineComment] = "Secret used to authenticate the bootstrapping node."

View File

@ -14,6 +14,8 @@ import (
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
var defaultState = &State{
@ -326,3 +328,94 @@ func TestMerge(t *testing.T) {
})
}
}
func TestMarshalHexBytes(t *testing.T) {
testCases := map[string]struct {
in HexBytes
expected string
wantErr bool
}{
"success": {
in: []byte{0xab, 0xcd, 0xef},
expected: "abcdef\n",
},
"empty": {
in: []byte{},
expected: "\"\"\n",
},
"nil": {
in: nil,
expected: "\"\"\n",
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
actual, err := yaml.Marshal(tc.in)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.expected, string(actual))
}
})
}
}
func TestUnmarshalHexBytes(t *testing.T) {
testCases := map[string]struct {
in string
expected HexBytes
wantErr bool
}{
"success": {
in: "abcdef",
expected: []byte{0xab, 0xcd, 0xef},
},
"empty": {
in: "",
expected: nil,
},
"byte slice compat": {
in: "[0xab, 0xcd, 0xef]",
expected: []byte{0xab, 0xcd, 0xef},
},
"byte slice compat 2": {
in: "[00, 12, 34]",
expected: []byte{0x00, 0x0c, 0x22},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
var actual HexBytes
err := yaml.Unmarshal([]byte(tc.in), &actual)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.expected, actual)
}
})
}
}
func TestMarshalUnmarshalHexBytes(t *testing.T) {
in := HexBytes{0xab, 0xcd, 0xef}
expected := "abcdef\n"
actual, err := yaml.Marshal(in)
require.NoError(t, err)
assert.Equal(t, expected, string(actual))
var actual2 HexBytes
err = yaml.Unmarshal(actual, &actual2)
require.NoError(t, err)
assert.Equal(t, in, actual2)
}

View File

@ -106,6 +106,7 @@ go_test(
],
embed = [":terraform"],
deps = [
"//cli/internal/state",
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/file",

View File

@ -16,6 +16,7 @@ import (
"strings"
"testing"
"github.com/edgelesssys/constellation/v2/cli/internal/state"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
@ -477,7 +478,7 @@ func TestCreateCluster(t *testing.T) {
}
assert.NoError(err)
assert.Equal("192.0.2.100", infraState.ClusterEndpoint)
assert.Equal([]byte("initSecret"), infraState.InitSecret)
assert.Equal(state.HexBytes("initSecret"), infraState.InitSecret)
assert.Equal("12345abc", infraState.UID)
if tc.provider == cloudprovider.Azure {
assert.Equal(tc.expectedAttestationURL, infraState.Azure.AttestationURL)

2
go.mod
View File

@ -322,7 +322,7 @@ require (
go.starlark.net v0.0.0-20220223235035-243c74974e97 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/term v0.13.0 // indirect

4
go.sum
View File

@ -1274,8 +1274,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@ -274,7 +274,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect

View File

@ -1215,8 +1215,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

View File

@ -106,7 +106,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect

View File

@ -435,8 +435,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=