diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index ca0d6a2a583..36e094b7b6a 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -1197,7 +1197,7 @@ } }, "post": { - "description": "create a Endpoints", + "description": "create Endpoints", "consumes": [ "*/*" ], @@ -1405,7 +1405,7 @@ } }, "delete": { - "description": "delete a Endpoints", + "description": "delete Endpoints", "consumes": [ "*/*" ], @@ -1580,7 +1580,7 @@ } }, "post": { - "description": "create a Event", + "description": "create an Event", "consumes": [ "*/*" ], @@ -1788,7 +1788,7 @@ } }, "delete": { - "description": "delete a Event", + "description": "delete an Event", "consumes": [ "*/*" ], @@ -3353,7 +3353,7 @@ }, "/api/v1/namespaces/{namespace}/pods/{name}/eviction": { "post": { - "description": "create eviction of a Eviction", + "description": "create eviction of an Eviction", "consumes": [ "*/*" ], @@ -20515,7 +20515,7 @@ } }, "post": { - "description": "create a Ingress", + "description": "create an Ingress", "consumes": [ "*/*" ], @@ -20723,7 +20723,7 @@ } }, "delete": { - "description": "delete a Ingress", + "description": "delete an Ingress", "consumes": [ "*/*" ], diff --git a/api/openapi-spec/v1.json b/api/openapi-spec/v1.json index 3469799b09e..674365ea964 100644 --- a/api/openapi-spec/v1.json +++ b/api/openapi-spec/v1.json @@ -1107,7 +1107,7 @@ } }, "post": { - "description": "create a Endpoints", + "description": "create Endpoints", "consumes": [ "*/*" ], @@ -1303,7 +1303,7 @@ } }, "delete": { - "description": "delete a Endpoints", + "description": "delete Endpoints", "consumes": [ "*/*" ], @@ -1469,7 +1469,7 @@ } }, "post": { - "description": "create a Event", + "description": "create an Event", "consumes": [ "*/*" ], @@ -1665,7 +1665,7 @@ } }, "delete": { - "description": "delete a Event", + "description": "delete an Event", "consumes": [ "*/*" ], @@ -3143,7 +3143,7 @@ }, "/api/v1/namespaces/{namespace}/pods/{name}/eviction": { "post": { - "description": "create eviction of a Eviction", + "description": "create eviction of an Eviction", "consumes": [ "*/*" ], diff --git a/api/swagger-spec/extensions_v1beta1.json b/api/swagger-spec/extensions_v1beta1.json index c4ab0557a34..944757e49f3 100644 --- a/api/swagger-spec/extensions_v1beta1.json +++ b/api/swagger-spec/extensions_v1beta1.json @@ -3184,7 +3184,7 @@ { "type": "v1beta1.Ingress", "method": "POST", - "summary": "create a Ingress", + "summary": "create an Ingress", "nickname": "createNamespacedIngress", "parameters": [ { @@ -3578,7 +3578,7 @@ { "type": "unversioned.Status", "method": "DELETE", - "summary": "delete a Ingress", + "summary": "delete an Ingress", "nickname": "deleteNamespacedIngress", "parameters": [ { diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index 801079faaff..726754cff3f 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -1062,7 +1062,7 @@ { "type": "v1.Endpoints", "method": "POST", - "summary": "create a Endpoints", + "summary": "create Endpoints", "nickname": "createNamespacedEndpoints", "parameters": [ { @@ -1456,7 +1456,7 @@ { "type": "unversioned.Status", "method": "DELETE", - "summary": "delete a Endpoints", + "summary": "delete Endpoints", "nickname": "deleteNamespacedEndpoints", "parameters": [ { @@ -1851,7 +1851,7 @@ { "type": "v1.Event", "method": "POST", - "summary": "create a Event", + "summary": "create an Event", "nickname": "createNamespacedEvent", "parameters": [ { @@ -2245,7 +2245,7 @@ { "type": "unversioned.Status", "method": "DELETE", - "summary": "delete a Event", + "summary": "delete an Event", "nickname": "deleteNamespacedEvent", "parameters": [ { @@ -8615,7 +8615,7 @@ { "type": "v1alpha1.Eviction", "method": "POST", - "summary": "create eviction of a Eviction", + "summary": "create eviction of an Eviction", "nickname": "createNamespacedEvictionEviction", "parameters": [ { diff --git a/docs/api-reference/extensions/v1beta1/operations.html b/docs/api-reference/extensions/v1beta1/operations.html index 6d214ce51d8..a4e68b78fcd 100755 --- a/docs/api-reference/extensions/v1beta1/operations.html +++ b/docs/api-reference/extensions/v1beta1/operations.html @@ -5986,7 +5986,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

create a Ingress

+

create an Ingress

POST /apis/extensions/v1beta1/namespaces/{namespace}/ingresses
@@ -6367,7 +6367,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

delete a Ingress

+

delete an Ingress

DELETE /apis/extensions/v1beta1/namespaces/{namespace}/ingresses/{name}
diff --git a/docs/api-reference/v1/operations.html b/docs/api-reference/v1/operations.html index d29b3e3b51b..09f46e05d64 100755 --- a/docs/api-reference/v1/operations.html +++ b/docs/api-reference/v1/operations.html @@ -3078,7 +3078,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

create a Endpoints

+

create Endpoints

POST /api/v1/namespaces/{namespace}/endpoints
@@ -3459,7 +3459,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

delete a Endpoints

+

delete Endpoints

DELETE /api/v1/namespaces/{namespace}/endpoints/{name}
@@ -4027,7 +4027,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

create a Event

+

create an Event

POST /api/v1/namespaces/{namespace}/events
@@ -4408,7 +4408,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

delete a Event

+

delete an Event

DELETE /api/v1/namespaces/{namespace}/events/{name}
@@ -8311,7 +8311,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

create eviction of a Eviction

+

create eviction of an Eviction

POST /api/v1/namespaces/{namespace}/pods/{name}/eviction
diff --git a/federation/docs/api-reference/extensions/v1beta1/operations.html b/federation/docs/api-reference/extensions/v1beta1/operations.html index 590926348d8..7c292ae5ea8 100755 --- a/federation/docs/api-reference/extensions/v1beta1/operations.html +++ b/federation/docs/api-reference/extensions/v1beta1/operations.html @@ -4360,7 +4360,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

create a Ingress

+

create an Ingress

POST /apis/extensions/v1beta1/namespaces/{namespace}/ingresses
@@ -4741,7 +4741,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

delete a Ingress

+

delete an Ingress

DELETE /apis/extensions/v1beta1/namespaces/{namespace}/ingresses/{name}
diff --git a/pkg/apiserver/BUILD b/pkg/apiserver/BUILD index a37c91749ee..8981bc8d1d4 100644 --- a/pkg/apiserver/BUILD +++ b/pkg/apiserver/BUILD @@ -51,6 +51,7 @@ go_library( "//pkg/util/proxy:go_default_library", "//pkg/util/runtime:go_default_library", "//pkg/util/strategicpatch:go_default_library", + "//pkg/util/strings:go_default_library", "//pkg/util/wsstream:go_default_library", "//pkg/watch:go_default_library", "//pkg/watch/versioned:go_default_library", diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index a3920f2632c..d90beb8c332 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" + utilstrings "k8s.io/kubernetes/pkg/util/strings" "github.com/emicklei/go-restful" ) @@ -610,9 +611,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag handler = CreateResource(creater, reqScope, a.group.Typer, admit) } handler = metrics.InstrumentRouteFunc(action.Verb, resource, handler) - doc := "create a " + kind + article := utilstrings.GetArticleForNoun(kind, " ") + doc := "create" + article + kind if hasSubresource { - doc = "create " + subresource + " of a " + kind + doc = "create " + subresource + " of" + article + kind } route := ws.POST(action.Path).To(handler). Doc(doc). @@ -625,9 +627,10 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag addParams(route, action.Params) ws.Route(route) case "DELETE": // Delete a resource. - doc := "delete a " + kind + article := utilstrings.GetArticleForNoun(kind, " ") + doc := "delete" + article + kind if hasSubresource { - doc = "delete " + subresource + " of a " + kind + doc = "delete " + subresource + " of" + article + kind } handler := metrics.InstrumentRouteFunc(action.Verb, resource, DeleteResource(gracefulDeleter, isGracefulDeleter, reqScope, admit)) route := ws.DELETE(action.Path).To(handler). diff --git a/pkg/util/strings/strings.go b/pkg/util/strings/strings.go index 2bcb174afc7..8de8619b343 100644 --- a/pkg/util/strings/strings.go +++ b/pkg/util/strings/strings.go @@ -17,8 +17,10 @@ limitations under the License. package strings import ( + "fmt" "path" "strings" + "unicode" ) // Splits a fully qualified name and returns its namespace and name. @@ -45,3 +47,30 @@ func ShortenString(str string, n int) string { return str[:n] } } + +// GetArticleForNoun returns the article needed for the given noun. +func GetArticleForNoun(noun string, padding string) string { + if noun[len(noun)-2:] != "ss" && noun[len(noun)-1:] == "s" { + // Plurals don't have an article. + // Don't catch words like class + return fmt.Sprintf("%v", padding) + } + + article := "a" + if isVowel(rune(noun[0])) { + article = "an" + } + + return fmt.Sprintf("%s%s%s", padding, article, padding) +} + +// isVowel returns true if the rune is a vowel (case insensitive). +func isVowel(c rune) bool { + vowels := []rune{'a', 'e', 'i', 'o', 'u'} + for _, value := range vowels { + if value == unicode.ToLower(c) { + return true + } + } + return false +} diff --git a/pkg/util/strings/strings_test.go b/pkg/util/strings/strings_test.go index 13c72599565..c31ea5f5219 100644 --- a/pkg/util/strings/strings_test.go +++ b/pkg/util/strings/strings_test.go @@ -71,3 +71,73 @@ func TestShortenString(t *testing.T) { } } } + +func TestIsVowel(t *testing.T) { + tests := []struct { + name string + arg rune + want bool + }{ + { + name: "yes", + arg: 'E', + want: true, + }, + { + name: "no", + arg: 'n', + want: false, + }, + } + for _, tt := range tests { + if got := isVowel(tt.arg); got != tt.want { + t.Errorf("%q. IsVowel() = %v, want %v", tt.name, got, tt.want) + } + } +} + +func TestGetArticleForNoun(t *testing.T) { + type args struct { + } + tests := []struct { + noun string + padding string + want string + }{ + { + noun: "Frog", + padding: " ", + want: " a ", + }, + { + noun: "frogs", + padding: " ", + want: " ", + }, + { + noun: "apple", + padding: "", + want: "an", + }, + { + noun: "Apples", + padding: " ", + want: " ", + }, + { + noun: "Ingress", + padding: " ", + want: " an ", + }, + { + noun: "Class", + padding: " ", + want: " a ", + }, + } + for _, tt := range tests { + if got := GetArticleForNoun(tt.noun, tt.padding); got != tt.want { + t.Errorf("%q. GetArticleForNoun() = %v, want %v", tt.noun, got, tt.want) + } + } +}