feat: implement deprecation warnings in helm lint (#7986)

* feat: implement deprecation warnings in helm lint

Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>

* added more deprecated APIs

Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>
pull/8042/head
Matt Butcher 4 years ago committed by GitHub
parent c0edc586c5
commit bf9d629dc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -34,6 +34,9 @@ import (
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
// SaveDir saves a chart as files in a directory.
//
// This takes the chart name, and creates a new subdirectory inside of the given dest
// directory, writing the chart's contents to that subdirectory.
func SaveDir(c *chart.Chart, dest string) error {
// Create the chart directory
outdir := filepath.Join(dest, c.Name())

@ -0,0 +1,64 @@
/*
Copyright The Helm 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 rules // import "helm.sh/helm/v3/pkg/lint/rules"
import "fmt"
// deprecatedAPIs lists APIs that are deprecated (left) with suggested alternatives (right).
//
// An empty rvalue indicates that the API is completely deprecated.
var deprecatedAPIs = map[string]string{
"extensions/v1 Deployment": "apps/v1 Deployment",
"extensions/v1 DaemonSet": "apps/v1 DaemonSet",
"extensions/v1 ReplicaSet": "apps/v1 ReplicaSet",
"extensions/v1beta1 PodSecurityPolicy": "policy/v1beta1 PodSecurityPolicy",
"extensions/v1beta1 NetworkPolicy": "networking.k8s.io/v1beta1 NetworkPolicy",
"extensions/v1beta1 Ingress": "networking.k8s.io/v1beta1 Ingress",
"apps/v1beta1 Deployment": "apps/v1 Deployment",
"apps/v1beta1 StatefulSet": "apps/v1 StatefulSet",
"apps/v1beta1 DaemonSet": "apps/v1 DaemonSet",
"apps/v1beta1 ReplicaSet": "apps/v1 ReplicaSet",
"apps/v1beta2 Deployment": "apps/v1 Deployment",
"apps/v1beta2 StatefulSet": "apps/v1 StatefulSet",
"apps/v1beta2 DaemonSet": "apps/v1 DaemonSet",
"apps/v1beta2 ReplicaSet": "apps/v1 ReplicaSet",
}
// deprecatedAPIError indicates than an API is deprecated in Kubernetes
type deprecatedAPIError struct {
Deprecated string
Alternative string
}
func (e deprecatedAPIError) Error() string {
msg := fmt.Sprintf("the kind %q is deprecated", e.Deprecated)
if e.Alternative != "" {
msg += fmt.Sprintf(" in favor of %q", e.Alternative)
}
return msg
}
func validateNoDeprecations(resource *K8sYamlStruct) error {
gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind)
if alt, ok := deprecatedAPIs[gvk]; ok {
return deprecatedAPIError{
Deprecated: gvk,
Alternative: alt,
}
}
return nil
}

@ -0,0 +1,42 @@
/*
Copyright The Helm 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 rules // import "helm.sh/helm/v3/pkg/lint/rules"
import "testing"
func TestValidateNoDeprecations(t *testing.T) {
deprecated := &K8sYamlStruct{
APIVersion: "extensions/v1",
Kind: "Deployment",
}
err := validateNoDeprecations(deprecated)
if err == nil {
t.Fatal("Expected deprecated extension to be flagged")
}
depErr := err.(deprecatedAPIError)
if depErr.Alternative != "apps/v1 Deployment" {
t.Errorf("Expected %q to be replaced by %q", depErr.Deprecated, depErr.Alternative)
}
if err := validateNoDeprecations(&K8sYamlStruct{
APIVersion: "v1",
Kind: "Pod",
}); err != nil {
t.Errorf("Expected a v1 Pod to not be deprecated")
}
}

@ -131,6 +131,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
// on this linter run.
linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err))
linter.RunLinterRule(support.ErrorSev, path, validateMetadataName(&yamlStruct))
linter.RunLinterRule(support.ErrorSev, path, validateNoDeprecations(&yamlStruct))
}
}
}
@ -190,7 +191,9 @@ func validateNoReleaseTime(manifest []byte) error {
// DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within
// the rules package.
type K8sYamlStruct struct {
Metadata k8sYamlMetadata
APIVersion string `json:"apiVersion"`
Kind string
Metadata k8sYamlMetadata
}
type k8sYamlMetadata struct {

@ -22,6 +22,9 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/internal/test/ensure"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/lint/support"
)
@ -130,3 +133,44 @@ func TestValidateMetadataName(t *testing.T) {
}
}
}
func TestDeprecatedAPIFails(t *testing.T) {
mychart := chart.Chart{
Metadata: &chart.Metadata{
APIVersion: "v2",
Name: "failapi",
Version: "0.1.0",
Icon: "satisfy-the-linting-gods.gif",
},
Templates: []*chart.File{
{
Name: "templates/baddeployment.yaml",
Data: []byte("apiVersion: apps/v1beta1\nkind: Deployment\nmetadata:\n name: baddep"),
},
{
Name: "templates/goodsecret.yaml",
Data: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: goodsecret"),
},
},
}
tmpdir := ensure.TempDir(t)
defer os.RemoveAll(tmpdir)
if err := chartutil.SaveDir(&mychart, tmpdir); err != nil {
t.Fatal(err)
}
linter := support.Linter{ChartDir: filepath.Join(tmpdir, mychart.Name())}
Templates(&linter, values, namespace, strict)
if l := len(linter.Messages); l != 1 {
for i, msg := range linter.Messages {
t.Logf("Message %d: %s", i, msg)
}
t.Fatalf("Expected 1 lint error, got %d", l)
}
err := linter.Messages[0].Err.(deprecatedAPIError)
if err.Deprecated != "apps/v1beta1 Deployment" {
t.Errorf("Surprised to learn that %q is deprecated", err.Deprecated)
}
}

Loading…
Cancel
Save