Skip to content

Commit

Permalink
Improve apiVersion deprecation comments
Browse files Browse the repository at this point in the history
Replace the deprecation comments in the SDKs with more
relevant information, and emit warnings when resources
are created using deprecated apiVersions.
  • Loading branch information
lblackstone committed Aug 30, 2019
1 parent 8912a60 commit 5ed035b
Show file tree
Hide file tree
Showing 32 changed files with 408 additions and 176 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

### Improvements

- Warn for deprecated apiVersions.
(https://github.com/pulumi/pulumi-kubernetes/pull/779).

### Bug fixes

- Fix name collisions in the Charts/YAML Python packages
Expand Down
14 changes: 11 additions & 3 deletions pkg/gen/awaitComments.go → pkg/gen/additionalComments.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/pulumi/pulumi-kubernetes/pkg/await"
"github.com/pulumi/pulumi-kubernetes/pkg/kinds"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func timeoutComment(kind kinds.Kind) string {
Expand Down Expand Up @@ -51,7 +52,7 @@ time out and mark the resource update as Failed. You can override the default ti
by %s`, kind, timeoutStr, timeoutOverride)
}

func comments(kind kinds.Kind) string {
func awaitComments(kind kinds.Kind) string {
const preamble = `This resource waits until it is ready before registering success for
create/update and populating output properties from the current state of the resource.
The following conditions are used to determine whether the resource creation has
Expand Down Expand Up @@ -122,7 +123,7 @@ out. To work around this limitation, set 'pulumi.com/skipAwait: "true"' on
2. The value of '.status.updateRevision' matches '.status.currentRevision'.
`
default:
panic("comments: unhandled kind")
panic("awaitComments: unhandled kind")
}

comment += timeoutComment(kind)
Expand All @@ -135,8 +136,15 @@ func AwaitComment(kind string) string {
k := kinds.Kind(kind)
switch k {
case kinds.Deployment, kinds.Ingress, kinds.Job, kinds.Pod, kinds.Service, kinds.StatefulSet:
return prefix + comments(k)
return prefix + awaitComments(k)
default:
return ""
}
}

func ApiVersionComment(gvk schema.GroupVersionKind) string {
const template = `This apiVersion is not supported by Kubernetes 1.16+ clusters. Use %s instead.
`
return fmt.Sprintf(template, kinds.SuggestedApiVersion(gvk))
}
29 changes: 24 additions & 5 deletions pkg/gen/typegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,24 @@ func stripPrefix(name string) string {
return strings.TrimPrefix(name, prefix)
}

func fmtComment(comment interface{}, prefix string, bareRender bool, opts groupOpts) string {
func replaceDeprecationComment(comment string, gvk schema.GroupVersionKind, language language) string {
re := regexp.MustCompile(`DEPRECATED - .* is deprecated by .* for more information\.\s*`)

var replacement string
switch language {
case typescript:
replacement = "@deprecated " + ApiVersionComment(gvk)
case python:
replacement = "DEPRECATED - " + ApiVersionComment(gvk)
default:
panic(fmt.Sprintf("Unsupported language '%s'", language))
}
return re.ReplaceAllString(comment, replacement)
}

func fmtComment(
comment interface{}, prefix string, bareRender bool, opts groupOpts, gvk schema.GroupVersionKind,
) string {
if comment == nil {
return ""
}
Expand Down Expand Up @@ -294,6 +311,8 @@ func fmtComment(comment interface{}, prefix string, bareRender bool, opts groupO
`https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md`,
-1)

commentstr = replaceDeprecationComment(commentstr, gvk, opts.language)

split := strings.Split(commentstr, "\n")
var lines []string
for _, paragraph := range split {
Expand Down Expand Up @@ -721,8 +740,8 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro
}

return &Property{
comment: fmtComment(prop["description"], prefix, false, opts),
pythonConstructorComment: fmtComment(prop["description"], prefix+prefix+" ", true, opts),
comment: fmtComment(prop["description"], prefix, false, opts, d.gvk),
pythonConstructorComment: fmtComment(prop["description"], prefix+prefix+" ", true, opts, d.gvk),
propType: t,
pythonConstructorPropType: pyConstructorT,
name: propName,
Expand Down Expand Up @@ -781,8 +800,8 @@ func createGroups(definitionsJSON map[string]interface{}, opts groupOpts) []*Gro
kind: d.gvk.Kind,
// NOTE: This transformation assumes git users on Windows to set
// the "check in with UNIX line endings" setting.
comment: fmtComment(d.data["description"], " ", true, opts),
awaitComment: fmtComment(AwaitComment(d.gvk.Kind), prefix, true, opts),
comment: fmtComment(d.data["description"], " ", true, opts, d.gvk),
awaitComment: fmtComment(AwaitComment(d.gvk.Kind), prefix, true, opts, d.gvk),
properties: properties,
requiredInputProperties: requiredInputProperties,
optionalInputProperties: optionalInputProperties,
Expand Down
52 changes: 52 additions & 0 deletions pkg/kinds/deprecated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2016-2019, Pulumi Corporation.
//
// 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 kinds

import (
"k8s.io/apimachinery/pkg/runtime/schema"
)

func gvkStr(gvk schema.GroupVersionKind) string {
return gvk.GroupVersion().String() + "/" + gvk.Kind
}

// DeprecatedApiVersion returns true if the given GVK is deprecated in the most recent k8s release.
func DeprecatedApiVersion(gvk schema.GroupVersionKind) bool {
return SuggestedApiVersion(gvk) != gvkStr(gvk)
}

// SuggestedApiVersion returns a string with the suggested apiVersion for a given GVK.
// This is used to provide useful warning messages when a user creates a resource using a deprecated GVK.
func SuggestedApiVersion(gvk schema.GroupVersionKind) string {
switch gvk.GroupVersion() {
case schema.GroupVersion{Group: "apps", Version: "v1beta1"},
schema.GroupVersion{Group: "apps", Version: "v1beta2"}:
return "apps/v1/" + gvk.Kind
case schema.GroupVersion{Group: "extensions", Version: "v1beta1"}:
switch Kind(gvk.Kind) {
case DaemonSet, Deployment, NetworkPolicy, ReplicaSet:
return "apps/v1/" + gvk.Kind
case Ingress:
return "networking/v1beta1/" + gvk.Kind
case PodSecurityPolicy:
return "policy/v1beta1/" + gvk.Kind
default:
return gvkStr(gvk)
}
default:
return gvkStr(gvk)
}

}
86 changes: 86 additions & 0 deletions pkg/kinds/deprecated_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2016-2019, Pulumi Corporation.
//
// 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 kinds

import (
"testing"

. "k8s.io/apimachinery/pkg/runtime/schema"
)

func TestDeprecatedApiVersion(t *testing.T) {
tests := []struct {
gvk GroupVersionKind
want bool
}{
{GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, false},
{GroupVersionKind{Group: "apps", Version: "v1beta1", Kind: "Deployment"}, true},
{GroupVersionKind{Group: "apps", Version: "v1beta2", Kind: "Deployment"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Ingress"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "NetworkPolicy"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicy"}, true},
{GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}, true},
}
for _, tt := range tests {
t.Run(tt.gvk.String(), func(t *testing.T) {
if got := DeprecatedApiVersion(tt.gvk); got != tt.want {
t.Errorf("DeprecatedApiVersion() = %v, want %v", got, tt.want)
}
})
}
}

func TestSuggestedApiVersion(t *testing.T) {
tests := []struct {
gvk GroupVersionKind
want string
}{
// Deprecated ApiVersions return the suggested version string.
{
GroupVersionKind{Group: "apps", Version: "v1beta1", Kind: "Deployment"},
"apps/v1/Deployment",
},
{
GroupVersionKind{Group: "apps", Version: "v1beta2", Kind: "Deployment"},
"apps/v1/Deployment",
},
{
GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Deployment"},
"apps/v1/Deployment",
},
{
GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Ingress"},
"networking/v1beta1/Ingress",
},
{
GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "PodSecurityPolicy"},
"policy/v1beta1/PodSecurityPolicy",
},
// Current ApiVersions return the same version string.
{
GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
"apps/v1/Deployment",
},
}
for _, tt := range tests {
t.Run(tt.gvk.String(), func(t *testing.T) {
if got := SuggestedApiVersion(tt.gvk); got != tt.want {
t.Errorf("SuggestedApiVersion() = %v, want %v", got, tt.want)
}
})
}
}
4 changes: 4 additions & 0 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ import (
pkgerrors "github.com/pkg/errors"
"github.com/pulumi/pulumi-kubernetes/pkg/await"
"github.com/pulumi/pulumi-kubernetes/pkg/clients"
"github.com/pulumi/pulumi-kubernetes/pkg/gen"
"github.com/pulumi/pulumi-kubernetes/pkg/logging"
"github.com/pulumi/pulumi-kubernetes/pkg/metadata"
"github.com/pulumi/pulumi-kubernetes/pkg/openapi"
"github.com/pulumi/pulumi/pkg/diag"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/plugin"
"github.com/pulumi/pulumi/pkg/resource/provider"
Expand Down Expand Up @@ -420,6 +422,8 @@ func (k *kubeProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (
return nil, err
}

_ = k.host.Log(ctx, diag.Warning, urn, gen.ApiVersionComment(gvk))

// If a default namespace is set on the provider for this resource, check if the resource has Namespaced
// or Global scope. For namespaced resources, set the namespace to the default value if unset.
if k.defaultNamespace != "" && len(newInputs.GetNamespace()) == 0 {
Expand Down
5 changes: 3 additions & 2 deletions sdk/nodejs/apps/v1beta1/ControllerRevision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of ControllerRevision is deprecated by
* apps/v1beta2/ControllerRevision. See the release notes for more information.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/ControllerRevision instead.
*
* ControllerRevision implements an immutable snapshot of state data. Clients are responsible
* for serializing and deserializing the objects that contain their internal state. Once a
* ControllerRevision has been successfully created, it can not be updated. The API Server will
Expand Down
7 changes: 4 additions & 3 deletions sdk/nodejs/apps/v1beta1/Deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of Deployment is deprecated by apps/v1beta2/Deployment. See
* the release notes for more information. Deployment enables declarative updates for Pods and
* ReplicaSets.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/Deployment instead.
*
* Deployment enables declarative updates for Pods and ReplicaSets.
*
* This resource waits until it is ready before registering success for
* create/update and populating output properties from the current state of the resource.
Expand Down
7 changes: 4 additions & 3 deletions sdk/nodejs/apps/v1beta1/StatefulSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of StatefulSet is deprecated by apps/v1beta2/StatefulSet. See
* the release notes for more information. StatefulSet represents a set of pods with consistent
* identities. Identities are defined as:
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/StatefulSet instead.
*
* StatefulSet represents a set of pods with consistent identities. Identities are defined as:
* - Network: A single stable DNS and hostname.
* - Storage: As many VolumeClaims as requested.
* The StatefulSet guarantees that a given network identity will always map to the same storage
Expand Down
21 changes: 11 additions & 10 deletions sdk/nodejs/apps/v1beta2/ControllerRevision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of ControllerRevision is deprecated by
* apps/v1/ControllerRevision. See the release notes for more information. ControllerRevision
* implements an immutable snapshot of state data. Clients are responsible for serializing and
* deserializing the objects that contain their internal state. Once a ControllerRevision has
* been successfully created, it can not be updated. The API Server will fail validation of all
* requests that attempt to mutate the Data field. ControllerRevisions may, however, be deleted.
* Note that, due to its use by both the DaemonSet and StatefulSet controllers for update and
* rollback, this object is beta. However, it may be subject to name and representation changes
* in future releases, and clients should not depend on its stability. It is primarily for
* internal use by controllers.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/ControllerRevision instead.
*
* ControllerRevision implements an immutable snapshot of state data. Clients are responsible
* for serializing and deserializing the objects that contain their internal state. Once a
* ControllerRevision has been successfully created, it can not be updated. The API Server will
* fail validation of all requests that attempt to mutate the Data field. ControllerRevisions
* may, however, be deleted. Note that, due to its use by both the DaemonSet and StatefulSet
* controllers for update and rollback, this object is beta. However, it may be subject to name
* and representation changes in future releases, and clients should not depend on its
* stability. It is primarily for internal use by controllers.
*/
export class ControllerRevision extends pulumi.CustomResource {
/**
Expand Down
6 changes: 4 additions & 2 deletions sdk/nodejs/apps/v1beta2/DaemonSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of DaemonSet is deprecated by apps/v1/DaemonSet. See the
* release notes for more information. DaemonSet represents the configuration of a daemon set.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/DaemonSet instead.
*
* DaemonSet represents the configuration of a daemon set.
*/
export class DaemonSet extends pulumi.CustomResource {
/**
Expand Down
7 changes: 4 additions & 3 deletions sdk/nodejs/apps/v1beta2/Deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of Deployment is deprecated by apps/v1/Deployment. See the
* release notes for more information. Deployment enables declarative updates for Pods and
* ReplicaSets.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/Deployment instead.
*
* Deployment enables declarative updates for Pods and ReplicaSets.
*
* This resource waits until it is ready before registering success for
* create/update and populating output properties from the current state of the resource.
Expand Down
7 changes: 4 additions & 3 deletions sdk/nodejs/apps/v1beta2/ReplicaSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1/ReplicaSet. See the
* release notes for more information. ReplicaSet ensures that a specified number of pod
* replicas are running at any given time.
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/ReplicaSet instead.
*
* ReplicaSet ensures that a specified number of pod replicas are running at any given time.
*/
export class ReplicaSet extends pulumi.CustomResource {
/**
Expand Down
7 changes: 4 additions & 3 deletions sdk/nodejs/apps/v1beta2/StatefulSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import * as outputs from "../../types/output";
import { getVersion } from "../../version";

/**
* DEPRECATED - This group version of StatefulSet is deprecated by apps/v1/StatefulSet. See the
* release notes for more information. StatefulSet represents a set of pods with consistent
* identities. Identities are defined as:
* @deprecated This apiVersion is not supported by Kubernetes 1.16+ clusters. Use
* apps/v1/StatefulSet instead.
*
* StatefulSet represents a set of pods with consistent identities. Identities are defined as:
* - Network: A single stable DNS and hostname.
* - Storage: As many VolumeClaims as requested.
* The StatefulSet guarantees that a given network identity will always map to the same storage
Expand Down
Loading

0 comments on commit 5ed035b

Please sign in to comment.