mirror of
https://github.com/rancher/steve.git
synced 2025-09-25 14:49:38 +00:00
2.8 helm data formatter (#307)
* changing secret and configmap formatters to return decoded helm data if includeHelmData query parameter is present * update gomod
This commit is contained in:
5
go.mod
5
go.mod
@@ -12,6 +12,7 @@ replace (
|
|||||||
require (
|
require (
|
||||||
github.com/adrg/xdg v0.4.0
|
github.com/adrg/xdg v0.4.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/pborman/uuid v1.2.1
|
github.com/pborman/uuid v1.2.1
|
||||||
@@ -28,17 +29,20 @@ require (
|
|||||||
github.com/urfave/cli v1.22.14
|
github.com/urfave/cli v1.22.14
|
||||||
github.com/urfave/cli/v2 v2.25.7
|
github.com/urfave/cli/v2 v2.25.7
|
||||||
golang.org/x/sync v0.5.0
|
golang.org/x/sync v0.5.0
|
||||||
|
helm.sh/helm/v3 v3.11.0
|
||||||
k8s.io/api v0.28.6
|
k8s.io/api v0.28.6
|
||||||
k8s.io/apiextensions-apiserver v0.28.6
|
k8s.io/apiextensions-apiserver v0.28.6
|
||||||
k8s.io/apimachinery v0.28.6
|
k8s.io/apimachinery v0.28.6
|
||||||
k8s.io/apiserver v0.28.6
|
k8s.io/apiserver v0.28.6
|
||||||
k8s.io/client-go v12.0.0+incompatible
|
k8s.io/client-go v12.0.0+incompatible
|
||||||
|
k8s.io/helm v2.17.0+incompatible
|
||||||
k8s.io/klog v1.0.0
|
k8s.io/klog v1.0.0
|
||||||
k8s.io/kube-aggregator v0.28.6
|
k8s.io/kube-aggregator v0.28.6
|
||||||
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9
|
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
@@ -55,7 +59,6 @@ require (
|
|||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.3 // indirect
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // 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.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
6
go.sum
6
go.sum
@@ -35,6 +35,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
@@ -792,6 +794,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
helm.sh/helm/v3 v3.11.0 h1:F+peaCQYbycY1FIqIQ6dAortHd/VzV5FkhMciv4Kf+c=
|
||||||
|
helm.sh/helm/v3 v3.11.0/go.mod h1:z/Bu/BylToGno/6dtNGuSmjRqxKq5gaH+FU0BPO+AQ8=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@@ -812,6 +816,8 @@ k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo=
|
|||||||
k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E=
|
k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E=
|
||||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||||
|
k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao=
|
||||||
|
k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI=
|
||||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
|
@@ -1,17 +1,59 @@
|
|||||||
package formatters
|
package formatters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/rancher/apiserver/pkg/types"
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
"github.com/rancher/norman/types/convert"
|
"github.com/rancher/norman/types/convert"
|
||||||
|
"github.com/rancher/wrangler/v2/pkg/data"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"helm.sh/helm/v3/pkg/release"
|
||||||
|
rspb "k8s.io/helm/pkg/proto/hapi/release"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DropHelmData(request *types.APIRequest, resource *types.RawResource) {
|
var (
|
||||||
data := resource.APIObject.Data()
|
ErrNotHelmRelease = errors.New("not helm release") // error for when it's not a helm release
|
||||||
if data.String("metadata", "labels", "owner") == "helm" ||
|
magicGzip = []byte{0x1f, 0x8b, 0x08} // gzip magic header
|
||||||
data.String("metadata", "labels", "OWNER") == "TILLER" {
|
)
|
||||||
if data.String("data", "release") != "" {
|
|
||||||
delete(data.Map("data"), "release")
|
func HandleHelmData(request *types.APIRequest, resource *types.RawResource) {
|
||||||
|
objData := resource.APIObject.Data()
|
||||||
|
if q := request.Query.Get("includeHelmData"); q == "true" {
|
||||||
|
var helmReleaseData string
|
||||||
|
if resource.Type == "secret" {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(objData.String("data", "release"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helmReleaseData = string(b)
|
||||||
|
} else {
|
||||||
|
helmReleaseData = objData.String("data", "release")
|
||||||
}
|
}
|
||||||
|
if objData.String("metadata", "labels", "owner") == "helm" {
|
||||||
|
rl, err := decodeHelm3(helmReleaseData)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to decode helm3 release data: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objData.SetNested(rl, "data", "release")
|
||||||
|
}
|
||||||
|
if objData.String("metadata", "labels", "OWNER") == "TILLER" {
|
||||||
|
rl, err := decodeHelm2(helmReleaseData)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to decode helm2 release data: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objData.SetNested(rl, "data", "release")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DropHelmData(objData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,3 +64,78 @@ func Pod(request *types.APIRequest, resource *types.RawResource) {
|
|||||||
data.SetNested(convert.LowerTitle(fields[2]), "metadata", "state", "name")
|
data.SetNested(convert.LowerTitle(fields[2]), "metadata", "state", "name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decodeHelm3 receives a helm3 release data string, decodes the string data using the standard base64 library
|
||||||
|
// and unmarshals the data into release.Release struct to return it.
|
||||||
|
func decodeHelm3(data string) (*release.Release, error) {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data is too small to be helm 3 release object
|
||||||
|
if len(b) <= 3 {
|
||||||
|
return nil, ErrNotHelmRelease
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backwards compatibility with releases that were stored before
|
||||||
|
// compression was introduced we skip decompression if the
|
||||||
|
// gzip magic header is not found
|
||||||
|
if bytes.Equal(b[0:3], magicGzip) {
|
||||||
|
r, err := gzip.NewReader(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b2, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = b2
|
||||||
|
}
|
||||||
|
|
||||||
|
var rls release.Release
|
||||||
|
// unmarshal release object bytes
|
||||||
|
if err := json.Unmarshal(b, &rls); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeHelm2 receives a helm2 release data and returns the corresponding helm2 release proto struct
|
||||||
|
func decodeHelm2(data string) (*rspb.Release, error) {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// For backwards compatibility with releases that were stored before
|
||||||
|
// compression was introduced we skip decompression if the
|
||||||
|
// gzip magic header is not found
|
||||||
|
if bytes.Equal(b[0:3], magicGzip) {
|
||||||
|
r, err := gzip.NewReader(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b2, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = b2
|
||||||
|
}
|
||||||
|
|
||||||
|
var rls rspb.Release
|
||||||
|
// unmarshal protobuf bytes
|
||||||
|
if err := proto.Unmarshal(b, &rls); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DropHelmData(data data.Object) {
|
||||||
|
if data.String("metadata", "labels", "owner") == "helm" ||
|
||||||
|
data.String("metadata", "labels", "OWNER") == "TILLER" {
|
||||||
|
if data.String("data", "release") != "" {
|
||||||
|
delete(data.Map("data"), "release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
380
pkg/resources/formatters/formatter_test.go
Normal file
380
pkg/resources/formatters/formatter_test.go
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
package formatters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"helm.sh/helm/v3/pkg/chart"
|
||||||
|
"helm.sh/helm/v3/pkg/release"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
pbchart "k8s.io/helm/pkg/proto/hapi/chart"
|
||||||
|
rspb "k8s.io/helm/pkg/proto/hapi/release"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var r = release.Release{
|
||||||
|
Name: "helmV3Release",
|
||||||
|
Chart: &chart.Chart{
|
||||||
|
Values: map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Version: 1,
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
var rv2 = rspb.Release{
|
||||||
|
Name: "helmV3Release",
|
||||||
|
Chart: &pbchart.Chart{
|
||||||
|
Metadata: &pbchart.Metadata{
|
||||||
|
Name: "chartName",
|
||||||
|
Version: "1.0.0",
|
||||||
|
},
|
||||||
|
Values: &pbchart.Config{
|
||||||
|
Values: map[string]*pbchart.Value{
|
||||||
|
"key": {Value: "value"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Version: 1,
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_HandleHelmData(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
resource *types.RawResource
|
||||||
|
request *types.APIRequest
|
||||||
|
want *types.RawResource
|
||||||
|
helmVersion int
|
||||||
|
}{ //helm v3
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource with includeHelmData set to TRUE and owner set to HELM, it should decode the helm3 release",
|
||||||
|
resource: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV3Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": &r,
|
||||||
|
}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource with includeHelmData set to FALSE and owner set to HELM, it should drop the helm data",
|
||||||
|
resource: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV3Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest("false"),
|
||||||
|
want: newSecret("helm", map[string]interface{}{}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource WITHOUT the includeHelmData query parameter and owner set to HELM, it should drop the helm data",
|
||||||
|
resource: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV3Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest(""),
|
||||||
|
want: newSecret("helm", map[string]interface{}{}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a non-helm SECRET or CONFIGMAP resource with includeHelmData set to TRUE, it shouldn't change the resource",
|
||||||
|
resource: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a non-helm SECRET or CONFIGMAP resource with includeHelmData set to FALSE, it shouldn't change the resource",
|
||||||
|
resource: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
request: newRequest("false"),
|
||||||
|
want: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a non-helm SECRET or CONFIGMAP resource WITHOUT the includeHelmData query parameter, it shouldn't change the resource",
|
||||||
|
resource: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
request: newRequest(""),
|
||||||
|
want: &types.RawResource{
|
||||||
|
Type: "pod",
|
||||||
|
APIObject: types.APIObject{Object: &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource with includeHelmData set to TRUE and owner set to HELM, it should decode the helm3 release",
|
||||||
|
resource: newConfigMap("helm", map[string]interface{}{
|
||||||
|
"release": newV3Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newConfigMap("helm", map[string]interface{}{
|
||||||
|
"release": &r,
|
||||||
|
}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource with includeHelmData set to FALSE and owner set to HELM, it should drop the helm data",
|
||||||
|
resource: newConfigMap("helm", map[string]interface{}{
|
||||||
|
"release": newV3Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest("false"),
|
||||||
|
want: newConfigMap("helm", map[string]interface{}{}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource WITHOUT the includeHelmData query parameter and owner set to HELM, it should drop the helm data",
|
||||||
|
resource: newConfigMap("helm", map[string]interface{}{
|
||||||
|
"release": newV3Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest(""),
|
||||||
|
want: newConfigMap("helm", map[string]interface{}{}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
//helm v2
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource with includeHelmData set to TRUE and owner set to TILLER, it should decode the helm2 release",
|
||||||
|
resource: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV2Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": &rv2,
|
||||||
|
}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource with includeHelmData set to FALSE and owner set to TILLER, it should drop the helm data",
|
||||||
|
resource: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV2Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest("false"),
|
||||||
|
want: newSecret("TILLER", map[string]interface{}{}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a SECRET resource WITHOUT the includeHelmData query parameter and owner set to TILLER, it should drop the helm data",
|
||||||
|
resource: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV2Release())),
|
||||||
|
}),
|
||||||
|
request: newRequest(""),
|
||||||
|
want: newSecret("TILLER", map[string]interface{}{}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource with includeHelmData set to TRUE and owner set to TILLER, it should decode the helm2 release",
|
||||||
|
resource: newConfigMap("TILLER", map[string]interface{}{
|
||||||
|
"release": newV2Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newConfigMap("TILLER", map[string]interface{}{
|
||||||
|
"release": &rv2,
|
||||||
|
}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource with includeHelmData set to FALSE and owner set to TILLER, it should drop the helm data",
|
||||||
|
resource: newConfigMap("TILLER", map[string]interface{}{
|
||||||
|
"release": newV2Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest("false"),
|
||||||
|
want: newConfigMap("TILLER", map[string]interface{}{}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When receiving a CONFIGMAP resource WITHOUT the includeHelmData query parameter and owner set to TILLER, it should drop the helm data",
|
||||||
|
resource: newConfigMap("TILLER", map[string]interface{}{
|
||||||
|
"release": newV2Release(),
|
||||||
|
}),
|
||||||
|
request: newRequest(""),
|
||||||
|
want: newConfigMap("TILLER", map[string]interface{}{}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "[no magic gzip] When receiving a SECRET resource with includeHelmData set to TRUE and owner set to HELM, it should decode the helm3 release",
|
||||||
|
resource: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV3ReleaseWithoutGzip())),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newSecret("helm", map[string]interface{}{
|
||||||
|
"release": &r,
|
||||||
|
}),
|
||||||
|
helmVersion: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "[no magic gzip] When receiving a SECRET resource with includeHelmData set to TRUE and owner set to TILLER, it should decode the helm2 release",
|
||||||
|
resource: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": base64.StdEncoding.EncodeToString([]byte(newV2ReleaseWithoutGzip())),
|
||||||
|
}),
|
||||||
|
request: newRequest("true"),
|
||||||
|
want: newSecret("TILLER", map[string]interface{}{
|
||||||
|
"release": &rv2,
|
||||||
|
}),
|
||||||
|
helmVersion: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
HandleHelmData(tt.request, tt.resource)
|
||||||
|
if tt.helmVersion == 2 {
|
||||||
|
u, ok := tt.resource.APIObject.Object.(*unstructured.Unstructured)
|
||||||
|
assert.True(t, ok)
|
||||||
|
rl, ok := u.UnstructuredContent()["data"].(map[string]interface{})["release"]
|
||||||
|
if ok {
|
||||||
|
u, ok = tt.want.APIObject.Object.(*unstructured.Unstructured)
|
||||||
|
assert.True(t, ok)
|
||||||
|
rl2, ok := u.UnstructuredContent()["data"].(map[string]interface{})["release"]
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.True(t, proto.Equal(rl.(proto.Message), rl2.(proto.Message)))
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tt.resource, tt.want)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, tt.resource, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSecret(owner string, data map[string]interface{}) *types.RawResource {
|
||||||
|
secret := &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Secret",
|
||||||
|
"data": data,
|
||||||
|
}}
|
||||||
|
if owner == "helm" {
|
||||||
|
secret.SetLabels(map[string]string{"owner": owner})
|
||||||
|
}
|
||||||
|
if owner == "TILLER" {
|
||||||
|
secret.SetLabels(map[string]string{"OWNER": owner})
|
||||||
|
}
|
||||||
|
return &types.RawResource{
|
||||||
|
Type: "secret",
|
||||||
|
APIObject: types.APIObject{Object: secret},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfigMap(owner string, data map[string]interface{}) *types.RawResource {
|
||||||
|
cfgMap := &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "configmap",
|
||||||
|
"data": data,
|
||||||
|
}}
|
||||||
|
if owner == "helm" {
|
||||||
|
cfgMap.SetLabels(map[string]string{"owner": owner})
|
||||||
|
}
|
||||||
|
if owner == "TILLER" {
|
||||||
|
cfgMap.SetLabels(map[string]string{"OWNER": owner})
|
||||||
|
}
|
||||||
|
return &types.RawResource{
|
||||||
|
Type: "configmap",
|
||||||
|
APIObject: types.APIObject{Object: cfgMap},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newV2Release() string {
|
||||||
|
a := rv2
|
||||||
|
b, err := proto.Marshal(&a)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to marshal release: %v", err)
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
gz := gzip.NewWriter(&buf)
|
||||||
|
gz.Write(b)
|
||||||
|
gz.Close()
|
||||||
|
return base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newV2ReleaseWithoutGzip() string {
|
||||||
|
a := rv2
|
||||||
|
b, err := proto.Marshal(&a)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to marshal release: %v", err)
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newV3Release() string {
|
||||||
|
b, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to marshal release: %v", err)
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
gz := gzip.NewWriter(&buf)
|
||||||
|
gz.Write(b)
|
||||||
|
gz.Close()
|
||||||
|
return base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newV3ReleaseWithoutGzip() string {
|
||||||
|
b, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to marshal release: %v", err)
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRequest(value string) *types.APIRequest {
|
||||||
|
req := &types.APIRequest{Query: url.Values{}}
|
||||||
|
if value != "" {
|
||||||
|
req.Query.Add("includeHelmData", value)
|
||||||
|
}
|
||||||
|
return req
|
||||||
|
}
|
@@ -54,11 +54,11 @@ func DefaultSchemaTemplates(cf *client.Factory,
|
|||||||
apigroups.Template(discovery),
|
apigroups.Template(discovery),
|
||||||
{
|
{
|
||||||
ID: "configmap",
|
ID: "configmap",
|
||||||
Formatter: formatters.DropHelmData,
|
Formatter: formatters.HandleHelmData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "secret",
|
ID: "secret",
|
||||||
Formatter: formatters.DropHelmData,
|
Formatter: formatters.HandleHelmData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "pod",
|
ID: "pod",
|
||||||
|
Reference in New Issue
Block a user