Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
249caa95
Commit
249caa95
authored
Sep 24, 2017
by
Antoine Pelisse
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rewrite `kubectl explain` to use openapi
This removes all dependencies on swagger 1.2 for explain.
parent
566364da
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
751 additions
and
259 deletions
+751
-259
BUILD
pkg/kubectl/BUILD
+1
-3
BUILD
pkg/kubectl/cmd/BUILD
+1
-0
explain.go
pkg/kubectl/cmd/explain.go
+11
-5
openapi.go
pkg/kubectl/cmd/util/openapi/openapi.go
+21
-0
explain.go
pkg/kubectl/explain.go
+0
-251
BUILD
pkg/kubectl/explain/BUILD
+34
-0
explain.go
pkg/kubectl/explain/explain.go
+64
-0
field_lookup.go
pkg/kubectl/explain/field_lookup.go
+107
-0
fields_printer.go
pkg/kubectl/explain/fields_printer.go
+84
-0
fields_printer_builder.go
pkg/kubectl/explain/fields_printer_builder.go
+36
-0
formatter.go
pkg/kubectl/explain/formatter.go
+120
-0
model_printer.go
pkg/kubectl/explain/model_printer.go
+129
-0
recursive_fields_printer.go
pkg/kubectl/explain/recursive_fields_printer.go
+77
-0
typename.go
pkg/kubectl/explain/typename.go
+66
-0
No files found.
pkg/kubectl/BUILD
View file @
249caa95
...
...
@@ -91,7 +91,6 @@ go_library(
"deployment.go",
"doc.go",
"env_file.go",
"explain.go",
"generate.go",
"history.go",
"interfaces.go",
...
...
@@ -118,7 +117,6 @@ go_library(
deps = [
"//federation/apis/federation/v1beta1:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/apps:go_default_library",
...
...
@@ -142,7 +140,6 @@ go_library(
"//pkg/kubectl/util/slice:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
...
...
@@ -196,6 +193,7 @@ filegroup(
":package-srcs",
"//pkg/kubectl/apps:all-srcs",
"//pkg/kubectl/cmd:all-srcs",
"//pkg/kubectl/explain:all-srcs",
"//pkg/kubectl/metricsutil:all-srcs",
"//pkg/kubectl/plugins:all-srcs",
"//pkg/kubectl/proxy:all-srcs",
...
...
pkg/kubectl/cmd/BUILD
View file @
249caa95
...
...
@@ -84,6 +84,7 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/editor:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/explain:go_default_library",
"//pkg/kubectl/metricsutil:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/proxy:go_default_library",
...
...
pkg/kubectl/cmd/explain.go
View file @
249caa95
...
...
@@ -24,9 +24,9 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/explain"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
...
...
@@ -80,7 +80,7 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
// TODO: After we figured out the new syntax to separate group and resource, allow
// the users to use it in explain (kubectl explain <group><syntax><resource>).
// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
inModel
,
fieldsPath
,
err
:=
kubectl
.
SplitAndParseResourceRequest
(
args
[
0
],
mapper
)
inModel
,
fieldsPath
,
err
:=
explain
.
SplitAndParseResourceRequest
(
args
[
0
],
mapper
)
if
err
!=
nil
{
return
err
}
...
...
@@ -108,14 +108,20 @@ func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, ar
}
else
{
apiVersion
,
err
=
schema
.
ParseGroupVersion
(
apiVersionString
)
if
err
!=
nil
{
return
nil
return
err
}
}
gvk
=
apiVersion
.
WithKind
(
gvk
.
Kind
)
schema
,
err
:=
f
.
SwaggerSchema
(
apiVersion
.
WithKind
(
gvk
.
Kind
)
)
resources
,
err
:=
f
.
OpenAPISchema
(
)
if
err
!=
nil
{
return
err
}
return
kubectl
.
PrintModelDescription
(
inModel
,
fieldsPath
,
out
,
schema
,
recursive
)
schema
:=
resources
.
LookupResource
(
gvk
)
if
schema
==
nil
{
return
fmt
.
Errorf
(
"Couldn't find resource for %q"
,
gvk
)
}
return
explain
.
PrintModelDescription
(
fieldsPath
,
out
,
schema
,
recursive
)
}
pkg/kubectl/cmd/util/openapi/openapi.go
View file @
249caa95
...
...
@@ -18,6 +18,7 @@ package openapi
import
(
"fmt"
"sort"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
...
...
@@ -181,6 +182,26 @@ func (k *Kind) GetName() string {
return
fmt
.
Sprintf
(
"Kind(%v)"
,
properties
)
}
// IsRequired returns true if `field` is a required field for this type.
func
(
k
*
Kind
)
IsRequired
(
field
string
)
bool
{
for
_
,
f
:=
range
k
.
RequiredFields
{
if
f
==
field
{
return
true
}
}
return
false
}
// Keys returns a alphabetically sorted list of keys.
func
(
k
*
Kind
)
Keys
()
[]
string
{
keys
:=
make
([]
string
,
0
)
for
key
:=
range
k
.
Fields
{
keys
=
append
(
keys
,
key
)
}
sort
.
Strings
(
keys
)
return
keys
}
// Map is an object who values must all be of the same `SubType`.
// The key of the object is always of type `string`.
type
Map
struct
{
...
...
pkg/kubectl/explain.go
deleted
100644 → 0
View file @
566364da
/*
Copyright 2014 The Kubernetes 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
kubectl
import
(
"fmt"
"io"
"strings"
"github.com/emicklei/go-restful-swagger12"
"k8s.io/apimachinery/pkg/api/meta"
apiutil
"k8s.io/kubernetes/pkg/api/util"
)
var
allModels
=
make
(
map
[
string
]
*
swagger
.
NamedModel
)
// SplitAndParseResourceRequest separates the users input into a model and fields
func
SplitAndParseResourceRequest
(
inResource
string
,
mapper
meta
.
RESTMapper
)
(
string
,
[]
string
,
error
)
{
inResource
,
fieldsPath
:=
splitDotNotation
(
inResource
)
inResource
,
_
=
mapper
.
ResourceSingularizer
(
inResource
)
return
inResource
,
fieldsPath
,
nil
}
// PrintModelDescription prints the description of a specific model or dot path.
// If recursive, all components nested within the fields of the schema will be
// printed.
func
PrintModelDescription
(
inModel
string
,
fieldsPath
[]
string
,
w
io
.
Writer
,
swaggerSchema
*
swagger
.
ApiDeclaration
,
recursive
bool
)
error
{
apiVer
:=
apiutil
.
GetVersion
(
swaggerSchema
.
ApiVersion
)
+
"."
var
pointedModel
*
swagger
.
NamedModel
for
i
:=
range
swaggerSchema
.
Models
.
List
{
name
:=
swaggerSchema
.
Models
.
List
[
i
]
.
Name
allModels
[
name
]
=
&
swaggerSchema
.
Models
.
List
[
i
]
if
strings
.
ToLower
(
name
)
==
strings
.
ToLower
(
apiVer
+
inModel
)
{
pointedModel
=
&
swaggerSchema
.
Models
.
List
[
i
]
}
}
if
pointedModel
==
nil
{
return
fmt
.
Errorf
(
"requested resource %q is not defined"
,
inModel
)
}
if
len
(
fieldsPath
)
==
0
{
return
printTopLevelResourceInfo
(
w
,
pointedModel
,
recursive
)
}
var
pointedModelAsProp
*
swagger
.
NamedModelProperty
for
_
,
field
:=
range
fieldsPath
{
if
prop
,
nextModel
,
isModel
:=
getField
(
pointedModel
,
field
);
prop
!=
nil
{
if
isModel
{
pointedModelAsProp
=
prop
pointedModel
=
allModels
[
nextModel
]
}
else
{
return
printPrimitive
(
w
,
prop
)
}
}
else
{
return
fmt
.
Errorf
(
"field %q does not exist"
,
field
)
}
}
return
printModelInfo
(
w
,
pointedModel
,
pointedModelAsProp
,
recursive
)
}
func
splitDotNotation
(
model
string
)
(
string
,
[]
string
)
{
var
fieldsPath
[]
string
dotModel
:=
strings
.
Split
(
model
,
"."
)
if
len
(
dotModel
)
>=
1
{
fieldsPath
=
dotModel
[
1
:
]
}
return
dotModel
[
0
],
fieldsPath
}
func
getPointedModel
(
prop
*
swagger
.
ModelProperty
)
(
string
,
bool
)
{
if
prop
.
Ref
!=
nil
{
return
*
prop
.
Ref
,
true
}
else
if
*
prop
.
Type
==
"array"
&&
prop
.
Items
.
Ref
!=
nil
{
return
*
prop
.
Items
.
Ref
,
true
}
return
""
,
false
}
func
getField
(
model
*
swagger
.
NamedModel
,
sField
string
)
(
*
swagger
.
NamedModelProperty
,
string
,
bool
)
{
for
_
,
prop
:=
range
model
.
Model
.
Properties
.
List
{
if
prop
.
Name
==
sField
{
pointedModel
,
isModel
:=
getPointedModel
(
&
prop
.
Property
)
return
&
prop
,
pointedModel
,
isModel
}
}
return
nil
,
""
,
false
}
func
printModelInfo
(
w
io
.
Writer
,
model
*
swagger
.
NamedModel
,
modelProp
*
swagger
.
NamedModelProperty
,
recursive
bool
)
error
{
t
,
_
:=
getFieldType
(
&
modelProp
.
Property
)
fmt
.
Fprintf
(
w
,
"RESOURCE: %s <%s>
\n\n
"
,
modelProp
.
Name
,
t
)
fieldDesc
,
_
:=
wrapAndIndentText
(
modelProp
.
Property
.
Description
,
" "
,
80
)
fmt
.
Fprintf
(
w
,
"DESCRIPTION:
\n
%s
\n\n
%s
\n
"
,
fieldDesc
,
indentText
(
model
.
Model
.
Description
,
" "
))
return
printFields
(
w
,
model
,
recursive
)
}
func
printPrimitive
(
w
io
.
Writer
,
field
*
swagger
.
NamedModelProperty
)
error
{
t
,
_
:=
getFieldType
(
&
field
.
Property
)
fmt
.
Fprintf
(
w
,
"FIELD: %s <%s>
\n\n
"
,
field
.
Name
,
t
)
d
,
_
:=
wrapAndIndentText
(
field
.
Property
.
Description
,
" "
,
80
)
fmt
.
Fprintf
(
w
,
"DESCRIPTION:
\n
%s
\n
"
,
d
)
return
nil
}
func
printTopLevelResourceInfo
(
w
io
.
Writer
,
model
*
swagger
.
NamedModel
,
recursive
bool
)
error
{
fmt
.
Fprintf
(
w
,
"DESCRIPTION:
\n
%s
\n
"
,
model
.
Model
.
Description
)
return
printFields
(
w
,
model
,
recursive
)
}
func
printFields
(
w
io
.
Writer
,
model
*
swagger
.
NamedModel
,
recursive
bool
)
error
{
fmt
.
Fprint
(
w
,
"
\n
FIELDS:
\n
"
)
for
_
,
field
:=
range
model
.
Model
.
Properties
.
List
{
fieldType
,
err
:=
getFieldType
(
&
field
.
Property
)
if
err
!=
nil
{
return
err
}
if
arrayContains
(
model
.
Model
.
Required
,
field
.
Name
)
{
fmt
.
Fprintf
(
w
,
" %s
\t
<%s> -required-
\n
"
,
field
.
Name
,
fieldType
)
}
else
{
fmt
.
Fprintf
(
w
,
" %s
\t
<%s>
\n
"
,
field
.
Name
,
fieldType
)
}
if
recursive
{
pointedModel
,
isModel
:=
getPointedModel
(
&
field
.
Property
)
if
isModel
{
for
_
,
nestedField
:=
range
allModels
[
pointedModel
]
.
Model
.
Properties
.
List
{
t
,
_
:=
getFieldType
(
&
nestedField
.
Property
)
fmt
.
Fprintf
(
w
,
" %s
\t
<%s>
\n
"
,
nestedField
.
Name
,
t
)
}
}
}
else
{
fieldDesc
,
_
:=
wrapAndIndentText
(
field
.
Property
.
Description
,
" "
,
80
)
fmt
.
Fprintf
(
w
,
"%s
\n\n
"
,
fieldDesc
)
}
}
fmt
.
Fprint
(
w
,
"
\n
"
)
return
nil
}
func
getFieldType
(
prop
*
swagger
.
ModelProperty
)
(
string
,
error
)
{
if
prop
.
Type
==
nil
{
return
"Object"
,
nil
}
else
if
*
prop
.
Type
==
"any"
{
// Swagger Spec doesn't return information for maps.
return
"map[string]string"
,
nil
}
else
if
*
prop
.
Type
==
"array"
{
if
prop
.
Items
==
nil
{
return
""
,
fmt
.
Errorf
(
"error in swagger spec. Property: %v contains an array without type"
,
prop
)
}
if
prop
.
Items
.
Ref
!=
nil
{
fieldType
:=
"[]Object"
return
fieldType
,
nil
}
fieldType
:=
"[]"
+
*
prop
.
Items
.
Type
return
fieldType
,
nil
}
return
*
prop
.
Type
,
nil
}
func
wrapAndIndentText
(
desc
,
indent
string
,
lim
int
)
(
string
,
error
)
{
words
:=
strings
.
Split
(
strings
.
Replace
(
strings
.
TrimSpace
(
desc
),
"
\n
"
,
" "
,
-
1
),
" "
)
n
:=
len
(
words
)
for
i
:=
0
;
i
<
n
;
i
++
{
if
len
(
words
[
i
])
>
lim
{
if
strings
.
Contains
(
words
[
i
],
"/"
)
{
s
:=
breakURL
(
words
[
i
])
words
=
append
(
words
[
:
i
],
append
(
s
,
words
[
i
+
1
:
]
...
)
...
)
i
=
i
+
len
(
s
)
-
1
}
else
{
fmt
.
Println
(
len
(
words
[
i
]))
return
""
,
fmt
.
Errorf
(
"there are words longer that the break limit is"
)
}
}
}
var
lines
[]
string
line
:=
[]
string
{
indent
}
lineL
:=
len
(
indent
)
for
i
:=
0
;
i
<
len
(
words
);
i
++
{
w
:=
words
[
i
]
if
strings
.
HasSuffix
(
w
,
"/"
)
&&
lineL
+
len
(
w
)
-
1
<
lim
{
prev
:=
line
[
len
(
line
)
-
1
]
if
strings
.
HasSuffix
(
prev
,
"/"
)
{
if
i
+
1
<
len
(
words
)
-
1
&&
!
strings
.
HasSuffix
(
words
[
i
+
1
],
"/"
)
{
w
=
strings
.
TrimSuffix
(
w
,
"/"
)
}
line
[
len
(
line
)
-
1
]
=
prev
+
w
lineL
+=
len
(
w
)
}
else
{
line
=
append
(
line
,
w
)
lineL
+=
len
(
w
)
+
1
}
}
else
if
lineL
+
len
(
w
)
<
lim
{
line
=
append
(
line
,
w
)
lineL
+=
len
(
w
)
+
1
}
else
{
lines
=
append
(
lines
,
strings
.
Join
(
line
,
" "
))
line
=
[]
string
{
indent
,
w
}
lineL
=
len
(
indent
)
+
len
(
w
)
}
}
lines
=
append
(
lines
,
strings
.
Join
(
line
,
" "
))
return
strings
.
Join
(
lines
,
"
\n
"
),
nil
}
func
breakURL
(
url
string
)
[]
string
{
var
buf
[]
string
for
_
,
part
:=
range
strings
.
Split
(
url
,
"/"
)
{
buf
=
append
(
buf
,
part
+
"/"
)
}
return
buf
}
func
indentText
(
text
,
indent
string
)
string
{
lines
:=
strings
.
Split
(
text
,
"
\n
"
)
for
i
:=
range
lines
{
lines
[
i
]
=
indent
+
lines
[
i
]
}
return
strings
.
Join
(
lines
,
"
\n
"
)
}
func
arrayContains
(
s
[]
string
,
e
string
)
bool
{
for
_
,
a
:=
range
s
{
if
a
==
e
{
return
true
}
}
return
false
}
pkg/kubectl/explain/BUILD
0 → 100644
View file @
249caa95
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"explain.go",
"field_lookup.go",
"fields_printer.go",
"fields_printer_builder.go",
"formatter.go",
"model_printer.go",
"recursive_fields_printer.go",
"typename.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
pkg/kubectl/explain/explain.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"io"
"strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
type
fieldsPrinter
interface
{
PrintFields
(
openapi
.
Schema
)
error
}
func
splitDotNotation
(
model
string
)
(
string
,
[]
string
)
{
var
fieldsPath
[]
string
dotModel
:=
strings
.
Split
(
model
,
"."
)
if
len
(
dotModel
)
>=
1
{
fieldsPath
=
dotModel
[
1
:
]
}
return
dotModel
[
0
],
fieldsPath
}
// SplitAndParseResourceRequest separates the users input into a model and fields
func
SplitAndParseResourceRequest
(
inResource
string
,
mapper
meta
.
RESTMapper
)
(
string
,
[]
string
,
error
)
{
inResource
,
fieldsPath
:=
splitDotNotation
(
inResource
)
inResource
,
_
=
mapper
.
ResourceSingularizer
(
inResource
)
return
inResource
,
fieldsPath
,
nil
}
// PrintModelDescription prints the description of a specific model or dot path.
// If recursive, all components nested within the fields of the schema will be
// printed.
func
PrintModelDescription
(
fieldsPath
[]
string
,
w
io
.
Writer
,
schema
openapi
.
Schema
,
recursive
bool
)
error
{
fieldName
:=
""
if
len
(
fieldsPath
)
!=
0
{
fieldName
=
fieldsPath
[
len
(
fieldsPath
)
-
1
]
}
// Go down the fieldsPath to find what we're trying to explain
schema
,
err
:=
LookupSchemaForField
(
schema
,
fieldsPath
)
if
err
!=
nil
{
return
err
}
b
:=
fieldsPrinterBuilder
{
Recursive
:
recursive
}
f
:=
&
Formatter
{
Writer
:
w
,
Wrap
:
80
}
return
PrintModel
(
fieldName
,
f
,
b
,
schema
)
}
pkg/kubectl/explain/field_lookup.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"fmt"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
// fieldLookup walks through a schema by following a path, and returns
// the final schema.
type
fieldLookup
struct
{
// Path to walk
Path
[]
string
// Return information: Schema found, or error.
Schema
openapi
.
Schema
Error
error
}
// SaveLeafSchema is used to detect if we are done walking the path, and
// saves the schema as a match.
func
(
f
*
fieldLookup
)
SaveLeafSchema
(
schema
openapi
.
Schema
)
bool
{
if
len
(
f
.
Path
)
!=
0
{
return
false
}
f
.
Schema
=
schema
return
true
}
// VisitArray is mostly a passthrough.
func
(
f
*
fieldLookup
)
VisitArray
(
a
*
openapi
.
Array
)
{
if
f
.
SaveLeafSchema
(
a
)
{
return
}
// Passthrough arrays.
a
.
SubType
.
Accept
(
f
)
}
// VisitMap is mostly a passthrough.
func
(
f
*
fieldLookup
)
VisitMap
(
m
*
openapi
.
Map
)
{
if
f
.
SaveLeafSchema
(
m
)
{
return
}
// Passthrough maps.
m
.
SubType
.
Accept
(
f
)
}
// VisitPrimitive stops the operation and returns itself as the found
// schema, even if it had more path to walk.
func
(
f
*
fieldLookup
)
VisitPrimitive
(
p
*
openapi
.
Primitive
)
{
// Even if Path is not empty (we're not expecting a leaf),
// return that primitive.
f
.
Schema
=
p
}
// VisitKind unstacks fields as it finds them.
func
(
f
*
fieldLookup
)
VisitKind
(
k
*
openapi
.
Kind
)
{
if
f
.
SaveLeafSchema
(
k
)
{
return
}
subSchema
,
ok
:=
k
.
Fields
[
f
.
Path
[
0
]]
if
!
ok
{
f
.
Error
=
fmt
.
Errorf
(
"field %q does not exist"
,
f
.
Path
[
0
])
return
}
f
.
Path
=
f
.
Path
[
1
:
]
subSchema
.
Accept
(
f
)
}
// VisitReference is mostly a passthrough.
func
(
f
*
fieldLookup
)
VisitReference
(
r
openapi
.
Reference
)
{
if
f
.
SaveLeafSchema
(
r
)
{
return
}
// Passthrough references.
r
.
SubSchema
()
.
Accept
(
f
)
}
// LookupSchemaForField looks for the schema of a given path in a base schema.
func
LookupSchemaForField
(
schema
openapi
.
Schema
,
path
[]
string
)
(
openapi
.
Schema
,
error
)
{
f
:=
&
fieldLookup
{
Path
:
path
}
schema
.
Accept
(
f
)
return
f
.
Schema
,
f
.
Error
}
pkg/kubectl/explain/fields_printer.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
// indentDesc is the level of indentation for descriptions.
const
indentDesc
=
2
// regularFieldsPrinter prints fields with their type and description.
type
regularFieldsPrinter
struct
{
Writer
*
Formatter
Error
error
}
var
_
openapi
.
SchemaVisitor
=
&
regularFieldsPrinter
{}
var
_
fieldsPrinter
=
&
regularFieldsPrinter
{}
// VisitArray prints a Array type. It is just a passthrough.
func
(
f
*
regularFieldsPrinter
)
VisitArray
(
a
*
openapi
.
Array
)
{
a
.
SubType
.
Accept
(
f
)
}
// VisitKind prints a Kind type. It prints each key in the kind, with
// the type, the required flag, and the description.
func
(
f
*
regularFieldsPrinter
)
VisitKind
(
k
*
openapi
.
Kind
)
{
for
_
,
key
:=
range
k
.
Keys
()
{
v
:=
k
.
Fields
[
key
]
required
:=
""
if
k
.
IsRequired
(
key
)
{
required
=
" -required-"
}
if
err
:=
f
.
Writer
.
Write
(
"%s
\t
<%s>%s"
,
key
,
GetTypeName
(
v
),
required
);
err
!=
nil
{
f
.
Error
=
err
return
}
if
err
:=
f
.
Writer
.
Indent
(
indentDesc
)
.
WriteWrapped
(
"%s"
,
v
.
GetDescription
());
err
!=
nil
{
f
.
Error
=
err
return
}
if
err
:=
f
.
Writer
.
Write
(
""
);
err
!=
nil
{
f
.
Error
=
err
return
}
}
}
// VisitMap prints a Map type. It is just a passthrough.
func
(
f
*
regularFieldsPrinter
)
VisitMap
(
m
*
openapi
.
Map
)
{
m
.
SubType
.
Accept
(
f
)
}
// VisitPrimitive prints a Primitive type. It stops the recursion.
func
(
f
*
regularFieldsPrinter
)
VisitPrimitive
(
p
*
openapi
.
Primitive
)
{
// Nothing to do. Shouldn't really happen.
}
// VisitReference prints a Reference type. It is just a passthrough.
func
(
f
*
regularFieldsPrinter
)
VisitReference
(
r
openapi
.
Reference
)
{
r
.
SubSchema
()
.
Accept
(
f
)
}
// PrintFields will write the types from schema.
func
(
f
*
regularFieldsPrinter
)
PrintFields
(
schema
openapi
.
Schema
)
error
{
schema
.
Accept
(
f
)
return
f
.
Error
}
pkg/kubectl/explain/fields_printer_builder.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
// fieldsPrinterBuilder builds either a regularFieldsPrinter or a
// recursiveFieldsPrinter based on the argument.
type
fieldsPrinterBuilder
struct
{
Recursive
bool
}
// BuildFieldsPrinter builds the appropriate fieldsPrinter.
func
(
f
fieldsPrinterBuilder
)
BuildFieldsPrinter
(
writer
*
Formatter
)
fieldsPrinter
{
if
f
.
Recursive
{
return
&
recursiveFieldsPrinter
{
Writer
:
writer
,
}
}
return
&
regularFieldsPrinter
{
Writer
:
writer
,
}
}
pkg/kubectl/explain/formatter.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"fmt"
"io"
"strings"
)
// Formatter helps you write with indentation, and can wrap text as needed.
type
Formatter
struct
{
IndentLevel
int
Wrap
int
Writer
io
.
Writer
}
// Indent creates a new Formatter that will indent the code by that much more.
func
(
f
Formatter
)
Indent
(
indent
int
)
*
Formatter
{
f
.
IndentLevel
=
f
.
IndentLevel
+
indent
return
&
f
}
// Write writes a string with the indentation set for the
// Formatter. This is not wrapping text.
func
(
f
*
Formatter
)
Write
(
str
string
,
a
...
interface
{})
error
{
// Don't indent empty lines
if
str
==
""
{
_
,
err
:=
io
.
WriteString
(
f
.
Writer
,
"
\n
"
)
return
err
}
indent
:=
""
for
i
:=
0
;
i
<
f
.
IndentLevel
;
i
++
{
indent
=
indent
+
" "
}
_
,
err
:=
io
.
WriteString
(
f
.
Writer
,
indent
+
fmt
.
Sprintf
(
str
,
a
...
)
+
"
\n
"
)
return
err
}
// WriteWrapped writes a string with the indentation set for the
// Formatter, and wraps as needed.
func
(
f
*
Formatter
)
WriteWrapped
(
str
string
,
a
...
interface
{})
error
{
if
f
.
Wrap
==
0
{
return
f
.
Write
(
str
,
a
...
)
}
text
:=
fmt
.
Sprintf
(
str
,
a
...
)
strs
:=
wrapString
(
text
,
f
.
Wrap
-
f
.
IndentLevel
)
for
_
,
substr
:=
range
strs
{
if
err
:=
f
.
Write
(
substr
);
err
!=
nil
{
return
err
}
}
return
nil
}
type
line
struct
{
wrap
int
words
[]
string
}
func
(
l
*
line
)
String
()
string
{
return
strings
.
Join
(
l
.
words
,
" "
)
}
func
(
l
*
line
)
Empty
()
bool
{
return
len
(
l
.
words
)
==
0
}
func
(
l
*
line
)
Len
()
int
{
return
len
(
l
.
String
())
}
// Add adds the word to the line, returns true if we could, false if we
// didn't have enough room. It's always possible to add to an empty line.
func
(
l
*
line
)
Add
(
word
string
)
bool
{
newLine
:=
line
{
wrap
:
l
.
wrap
,
words
:
append
(
l
.
words
,
word
),
}
if
newLine
.
Len
()
<=
l
.
wrap
||
len
(
l
.
words
)
==
0
{
l
.
words
=
newLine
.
words
return
true
}
return
false
}
func
wrapString
(
str
string
,
wrap
int
)
[]
string
{
words
:=
strings
.
Fields
(
str
)
wrapped
:=
[]
string
{}
l
:=
line
{
wrap
:
wrap
}
for
_
,
word
:=
range
words
{
if
l
.
Add
(
word
)
==
false
{
wrapped
=
append
(
wrapped
,
l
.
String
())
l
=
line
{
wrap
:
wrap
}
if
l
.
Add
(
word
)
==
false
{
panic
(
"Couldn't add to empty line."
)
}
}
}
if
!
l
.
Empty
()
{
wrapped
=
append
(
wrapped
,
l
.
String
())
}
return
wrapped
}
pkg/kubectl/explain/model_printer.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
// fieldIndentLevel is the level of indentation for fields.
const
fieldIndentLevel
=
3
// descriptionIndentLevel is the level of indentation for the
// description.
const
descriptionIndentLevel
=
5
// modelPrinter prints a schema in Writer. Its "Builder" will decide if
// it's recursive or not.
type
modelPrinter
struct
{
Name
string
Type
string
Descriptions
[]
string
Writer
*
Formatter
Builder
fieldsPrinterBuilder
Error
error
}
var
_
openapi
.
SchemaVisitor
=
&
modelPrinter
{}
// PrintDescription prints the description for a given schema. There
// might be multiple description, since we collect descriptions when we
// go through references, arrays and maps.
func
(
m
*
modelPrinter
)
PrintDescription
(
schema
openapi
.
Schema
)
error
{
if
err
:=
m
.
Writer
.
Write
(
"DESCRIPTION:"
);
err
!=
nil
{
return
err
}
for
i
,
desc
:=
range
append
(
m
.
Descriptions
,
schema
.
GetDescription
())
{
if
desc
==
""
{
continue
}
if
i
!=
0
{
if
err
:=
m
.
Writer
.
Write
(
""
);
err
!=
nil
{
return
err
}
}
if
err
:=
m
.
Writer
.
Indent
(
descriptionIndentLevel
)
.
WriteWrapped
(
desc
);
err
!=
nil
{
return
err
}
}
return
nil
}
// VisitArray recurses inside the subtype, while collecting the type if
// not done yet, and the description.
func
(
m
*
modelPrinter
)
VisitArray
(
a
*
openapi
.
Array
)
{
m
.
Descriptions
=
append
(
m
.
Descriptions
,
a
.
GetDescription
())
if
m
.
Type
==
""
{
m
.
Type
=
GetTypeName
(
a
)
}
a
.
SubType
.
Accept
(
m
)
}
// VisitKind prints a full resource with its fields.
func
(
m
*
modelPrinter
)
VisitKind
(
k
*
openapi
.
Kind
)
{
if
m
.
Type
==
""
{
m
.
Type
=
GetTypeName
(
k
)
}
if
m
.
Name
!=
""
{
m
.
Writer
.
Write
(
"RESOURCE: %s <%s>
\n
"
,
m
.
Name
,
m
.
Type
)
}
if
err
:=
m
.
PrintDescription
(
k
);
err
!=
nil
{
m
.
Error
=
err
return
}
if
err
:=
m
.
Writer
.
Write
(
"
\n
FIELDS:"
);
err
!=
nil
{
m
.
Error
=
err
return
}
m
.
Error
=
m
.
Builder
.
BuildFieldsPrinter
(
m
.
Writer
.
Indent
(
fieldIndentLevel
))
.
PrintFields
(
k
)
}
// VisitMap recurses inside the subtype, while collecting the type if
// not done yet, and the description.
func
(
m
*
modelPrinter
)
VisitMap
(
om
*
openapi
.
Map
)
{
m
.
Descriptions
=
append
(
m
.
Descriptions
,
om
.
GetDescription
())
if
m
.
Type
==
""
{
m
.
Type
=
GetTypeName
(
om
)
}
om
.
SubType
.
Accept
(
m
)
}
// VisitPrimitive prints a field type and its description.
func
(
m
*
modelPrinter
)
VisitPrimitive
(
p
*
openapi
.
Primitive
)
{
if
m
.
Type
==
""
{
m
.
Type
=
GetTypeName
(
p
)
}
if
err
:=
m
.
Writer
.
Write
(
"FIELD: %s <%s>
\n
"
,
m
.
Name
,
m
.
Type
);
err
!=
nil
{
m
.
Error
=
err
return
}
m
.
Error
=
m
.
PrintDescription
(
p
)
}
// VisitReference recurses inside the subtype, while collecting the description.
func
(
m
*
modelPrinter
)
VisitReference
(
r
openapi
.
Reference
)
{
m
.
Descriptions
=
append
(
m
.
Descriptions
,
r
.
GetDescription
())
r
.
SubSchema
()
.
Accept
(
m
)
}
// PrintModel prints the description of a schema in writer.
func
PrintModel
(
name
string
,
writer
*
Formatter
,
builder
fieldsPrinterBuilder
,
schema
openapi
.
Schema
)
error
{
m
:=
&
modelPrinter
{
Name
:
name
,
Writer
:
writer
,
Builder
:
builder
}
schema
.
Accept
(
m
)
return
m
.
Error
}
pkg/kubectl/explain/recursive_fields_printer.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
// indentPerLevel is the level of indentation for each field recursion.
const
indentPerLevel
=
3
// recursiveFieldsPrinter recursively prints all the fields for a given
// schema.
type
recursiveFieldsPrinter
struct
{
Writer
*
Formatter
Error
error
}
var
_
openapi
.
SchemaVisitor
=
&
recursiveFieldsPrinter
{}
var
_
fieldsPrinter
=
&
recursiveFieldsPrinter
{}
// VisitArray is just a passthrough.
func
(
f
*
recursiveFieldsPrinter
)
VisitArray
(
a
*
openapi
.
Array
)
{
a
.
SubType
.
Accept
(
f
)
}
// VisitKind prints all its fields with their type, and then recurses
// inside each of these (pre-order).
func
(
f
*
recursiveFieldsPrinter
)
VisitKind
(
k
*
openapi
.
Kind
)
{
for
_
,
key
:=
range
k
.
Keys
()
{
v
:=
k
.
Fields
[
key
]
f
.
Writer
.
Write
(
"%s
\t
<%s>"
,
key
,
GetTypeName
(
v
))
subFields
:=
&
recursiveFieldsPrinter
{
Writer
:
f
.
Writer
.
Indent
(
indentPerLevel
),
}
if
err
:=
subFields
.
PrintFields
(
v
);
err
!=
nil
{
f
.
Error
=
err
return
}
}
}
// VisitMap is just a passthrough.
func
(
f
*
recursiveFieldsPrinter
)
VisitMap
(
m
*
openapi
.
Map
)
{
m
.
SubType
.
Accept
(
f
)
}
// VisitPrimitive does nothing, since it doesn't have sub-fields.
func
(
f
*
recursiveFieldsPrinter
)
VisitPrimitive
(
p
*
openapi
.
Primitive
)
{
// Nothing to do.
}
// VisitReference is just a passthrough.
func
(
f
*
recursiveFieldsPrinter
)
VisitReference
(
r
openapi
.
Reference
)
{
r
.
SubSchema
()
.
Accept
(
f
)
}
// PrintFields will recursively print all the fields for the given
// schema.
func
(
f
*
recursiveFieldsPrinter
)
PrintFields
(
schema
openapi
.
Schema
)
error
{
schema
.
Accept
(
f
)
return
f
.
Error
}
pkg/kubectl/explain/typename.go
0 → 100644
View file @
249caa95
/*
Copyright 2017 The Kubernetes 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
explain
import
(
"fmt"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
)
// typeName finds the name of a schema
type
typeName
struct
{
Name
string
}
var
_
openapi
.
SchemaVisitor
=
&
typeName
{}
// VisitArray adds the [] prefix and recurses.
func
(
t
*
typeName
)
VisitArray
(
a
*
openapi
.
Array
)
{
s
:=
&
typeName
{}
a
.
SubType
.
Accept
(
s
)
t
.
Name
=
fmt
.
Sprintf
(
"[]%s"
,
s
.
Name
)
}
// VisitKind just returns "Object".
func
(
t
*
typeName
)
VisitKind
(
k
*
openapi
.
Kind
)
{
t
.
Name
=
"Object"
}
// VisitMap adds the map[string] prefix and recurses.
func
(
t
*
typeName
)
VisitMap
(
m
*
openapi
.
Map
)
{
s
:=
&
typeName
{}
m
.
SubType
.
Accept
(
s
)
t
.
Name
=
fmt
.
Sprintf
(
"map[string]%s"
,
s
.
Name
)
}
// VisitPrimitive returns the name of the primitive.
func
(
t
*
typeName
)
VisitPrimitive
(
p
*
openapi
.
Primitive
)
{
t
.
Name
=
p
.
Type
}
// VisitReference is just a passthrough.
func
(
t
*
typeName
)
VisitReference
(
r
openapi
.
Reference
)
{
r
.
SubSchema
()
.
Accept
(
t
)
}
// GetTypeName returns the type of a schema.
func
GetTypeName
(
schema
openapi
.
Schema
)
string
{
t
:=
&
typeName
{}
schema
.
Accept
(
t
)
return
t
.
Name
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment