mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
new kubectl explain command
This commit is contained in:
parent
495433fbb7
commit
e8b9dde623
@ -17,6 +17,7 @@ docs/man/man1/kubectl-delete.1
|
||||
docs/man/man1/kubectl-describe.1
|
||||
docs/man/man1/kubectl-edit.1
|
||||
docs/man/man1/kubectl-exec.1
|
||||
docs/man/man1/kubectl-explain.1
|
||||
docs/man/man1/kubectl-expose.1
|
||||
docs/man/man1/kubectl-get.1
|
||||
docs/man/man1/kubectl-label.1
|
||||
@ -50,6 +51,7 @@ docs/user-guide/kubectl/kubectl_delete.md
|
||||
docs/user-guide/kubectl/kubectl_describe.md
|
||||
docs/user-guide/kubectl/kubectl_edit.md
|
||||
docs/user-guide/kubectl/kubectl_exec.md
|
||||
docs/user-guide/kubectl/kubectl_explain.md
|
||||
docs/user-guide/kubectl/kubectl_expose.md
|
||||
docs/user-guide/kubectl/kubectl_get.md
|
||||
docs/user-guide/kubectl/kubectl_label.md
|
||||
|
@ -1105,6 +1105,22 @@ _kubectl_version()
|
||||
must_have_one_noun=()
|
||||
}
|
||||
|
||||
_kubectl_explain()
|
||||
{
|
||||
last_command="kubectl_explain"
|
||||
commands=()
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--recursive")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
}
|
||||
|
||||
_kubectl()
|
||||
{
|
||||
last_command="kubectl"
|
||||
@ -1133,6 +1149,7 @@ _kubectl()
|
||||
commands+=("cluster-info")
|
||||
commands+=("api-versions")
|
||||
commands+=("version")
|
||||
commands+=("explain")
|
||||
|
||||
flags=()
|
||||
two_word_flags=()
|
||||
|
@ -15,6 +15,7 @@ kubectl-delete.1
|
||||
kubectl-describe.1
|
||||
kubectl-edit.1
|
||||
kubectl-exec.1
|
||||
kubectl-explain.1
|
||||
kubectl-expose.1
|
||||
kubectl-get.1
|
||||
kubectl-label.1
|
||||
|
147
docs/man/man1/kubectl-explain.1
Normal file
147
docs/man/man1/kubectl-explain.1
Normal file
@ -0,0 +1,147 @@
|
||||
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
|
||||
|
||||
|
||||
.SH NAME
|
||||
.PP
|
||||
kubectl explain \- Documentation of resources.
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.PP
|
||||
\fBkubectl explain\fP [OPTIONS]
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Documentation of resources.
|
||||
|
||||
.PP
|
||||
Possible resource types include: pods (po), services (svc),
|
||||
replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs),
|
||||
limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc),
|
||||
resourcequotas (quota), namespaces (ns) or endpoints (ep).
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-recursive\fP=false
|
||||
Print the fields of fields (Currently only 1 level deep)
|
||||
|
||||
|
||||
.SH OPTIONS INHERITED FROM PARENT COMMANDS
|
||||
.PP
|
||||
\fB\-\-alsologtostderr\fP=false
|
||||
log to standard error as well as files
|
||||
|
||||
.PP
|
||||
\fB\-\-api\-version\fP=""
|
||||
The API version to use when talking to the server
|
||||
|
||||
.PP
|
||||
\fB\-\-certificate\-authority\fP=""
|
||||
Path to a cert. file for the certificate authority.
|
||||
|
||||
.PP
|
||||
\fB\-\-client\-certificate\fP=""
|
||||
Path to a client key file for TLS.
|
||||
|
||||
.PP
|
||||
\fB\-\-client\-key\fP=""
|
||||
Path to a client key file for TLS.
|
||||
|
||||
.PP
|
||||
\fB\-\-cluster\fP=""
|
||||
The name of the kubeconfig cluster to use
|
||||
|
||||
.PP
|
||||
\fB\-\-context\fP=""
|
||||
The name of the kubeconfig context to use
|
||||
|
||||
.PP
|
||||
\fB\-\-insecure\-skip\-tls\-verify\fP=false
|
||||
If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
|
||||
.PP
|
||||
\fB\-\-kubeconfig\fP=""
|
||||
Path to the kubeconfig file to use for CLI requests.
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-backtrace\-at\fP=:0
|
||||
when logging hits line file:N, emit a stack trace
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-dir\fP=""
|
||||
If non\-empty, write log files in this directory
|
||||
|
||||
.PP
|
||||
\fB\-\-log\-flush\-frequency\fP=5s
|
||||
Maximum number of seconds between log flushes
|
||||
|
||||
.PP
|
||||
\fB\-\-logtostderr\fP=true
|
||||
log to standard error instead of files
|
||||
|
||||
.PP
|
||||
\fB\-\-match\-server\-version\fP=false
|
||||
Require server version to match client version
|
||||
|
||||
.PP
|
||||
\fB\-\-namespace\fP=""
|
||||
If present, the namespace scope for this CLI request.
|
||||
|
||||
.PP
|
||||
\fB\-\-password\fP=""
|
||||
Password for basic authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-s\fP, \fB\-\-server\fP=""
|
||||
The address and port of the Kubernetes API server
|
||||
|
||||
.PP
|
||||
\fB\-\-stderrthreshold\fP=2
|
||||
logs at or above this threshold go to stderr
|
||||
|
||||
.PP
|
||||
\fB\-\-token\fP=""
|
||||
Bearer token for authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-\-user\fP=""
|
||||
The name of the kubeconfig user to use
|
||||
|
||||
.PP
|
||||
\fB\-\-username\fP=""
|
||||
Username for basic authentication to the API server.
|
||||
|
||||
.PP
|
||||
\fB\-\-v\fP=0
|
||||
log level for V logs
|
||||
|
||||
.PP
|
||||
\fB\-\-vmodule\fP=
|
||||
comma\-separated list of pattern=N settings for file\-filtered logging
|
||||
|
||||
|
||||
.SH EXAMPLE
|
||||
.PP
|
||||
.RS
|
||||
|
||||
.nf
|
||||
# Get the documentation of the resource and its fields
|
||||
$ kubectl explain pods
|
||||
|
||||
# Get the documentation of a specific field of a resource
|
||||
$ kubectl explain pods.spec.containers
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBkubectl(1)\fP,
|
||||
|
||||
|
||||
.SH HISTORY
|
||||
.PP
|
||||
January 2015, Originally compiled by Eric Paris (eparis at redhat dot com) based on the kubernetes source material, but hopefully they have been automatically generated since!
|
@ -116,7 +116,7 @@ Find more information at
|
||||
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
\fBkubectl\-get(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-replace(1)\fP, \fBkubectl\-patch(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-edit(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-attach(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-annotate(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP,
|
||||
\fBkubectl\-get(1)\fP, \fBkubectl\-describe(1)\fP, \fBkubectl\-create(1)\fP, \fBkubectl\-replace(1)\fP, \fBkubectl\-patch(1)\fP, \fBkubectl\-delete(1)\fP, \fBkubectl\-edit(1)\fP, \fBkubectl\-namespace(1)\fP, \fBkubectl\-logs(1)\fP, \fBkubectl\-rolling\-update(1)\fP, \fBkubectl\-scale(1)\fP, \fBkubectl\-attach(1)\fP, \fBkubectl\-exec(1)\fP, \fBkubectl\-port\-forward(1)\fP, \fBkubectl\-proxy(1)\fP, \fBkubectl\-run(1)\fP, \fBkubectl\-stop(1)\fP, \fBkubectl\-expose(1)\fP, \fBkubectl\-label(1)\fP, \fBkubectl\-annotate(1)\fP, \fBkubectl\-config(1)\fP, \fBkubectl\-cluster\-info(1)\fP, \fBkubectl\-api\-versions(1)\fP, \fBkubectl\-version(1)\fP, \fBkubectl\-explain(1)\fP,
|
||||
|
||||
|
||||
.SH HISTORY
|
||||
|
@ -16,6 +16,7 @@ kubectl_delete.md
|
||||
kubectl_describe.md
|
||||
kubectl_edit.md
|
||||
kubectl_exec.md
|
||||
kubectl_explain.md
|
||||
kubectl_expose.md
|
||||
kubectl_get.md
|
||||
kubectl_label.md
|
||||
|
@ -86,6 +86,7 @@ kubectl
|
||||
* [kubectl describe](kubectl_describe.md) - Show details of a specific resource or group of resources
|
||||
* [kubectl edit](kubectl_edit.md) - Edit a resource on the server
|
||||
* [kubectl exec](kubectl_exec.md) - Execute a command in a container.
|
||||
* [kubectl explain](kubectl_explain.md) - Documentation of resources.
|
||||
* [kubectl expose](kubectl_expose.md) - Take a replication controller, service or pod and expose it as a new Kubernetes Service
|
||||
* [kubectl get](kubectl_get.md) - Display one or many resources
|
||||
* [kubectl label](kubectl_label.md) - Update the labels on a resource
|
||||
@ -101,7 +102,7 @@ kubectl
|
||||
* [kubectl stop](kubectl_stop.md) - Deprecated: Gracefully shut down a resource by name or filename.
|
||||
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-09-11 06:17:55.670147499 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-09-22 11:13:47.6353025 +0000 UTC
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
[]()
|
||||
|
104
docs/user-guide/kubectl/kubectl_explain.md
Normal file
104
docs/user-guide/kubectl/kubectl_explain.md
Normal file
@ -0,0 +1,104 @@
|
||||
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
|
||||
|
||||
<!-- BEGIN STRIP_FOR_RELEASE -->
|
||||
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
|
||||
width="25" height="25">
|
||||
|
||||
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
|
||||
|
||||
If you are using a released version of Kubernetes, you should
|
||||
refer to the docs that go with that version.
|
||||
|
||||
<strong>
|
||||
The latest 1.0.x release of this document can be found
|
||||
[here](http://releases.k8s.io/release-1.0/docs/user-guide/kubectl/kubectl_explain.md).
|
||||
|
||||
Documentation for other releases can be found at
|
||||
[releases.k8s.io](http://releases.k8s.io).
|
||||
</strong>
|
||||
--
|
||||
|
||||
<!-- END STRIP_FOR_RELEASE -->
|
||||
|
||||
<!-- END MUNGE: UNVERSIONED_WARNING -->
|
||||
|
||||
## kubectl explain
|
||||
|
||||
Documentation of resources.
|
||||
|
||||
### Synopsis
|
||||
|
||||
|
||||
Documentation of resources.
|
||||
|
||||
Possible resource types include: pods (po), services (svc),
|
||||
replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs),
|
||||
limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc),
|
||||
resourcequotas (quota), namespaces (ns) or endpoints (ep).
|
||||
|
||||
```
|
||||
kubectl explain RESOURCE
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# Get the documentation of the resource and its fields
|
||||
$ kubectl explain pods
|
||||
|
||||
# Get the documentation of a specific field of a resource
|
||||
$ kubectl explain pods.spec.containers
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--recursive[=false]: Print the fields of fields (Currently only 1 level deep)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--alsologtostderr[=false]: log to standard error as well as files
|
||||
--api-version="": The API version to use when talking to the server
|
||||
--certificate-authority="": Path to a cert. file for the certificate authority.
|
||||
--client-certificate="": Path to a client key file for TLS.
|
||||
--client-key="": Path to a client key file for TLS.
|
||||
--cluster="": The name of the kubeconfig cluster to use
|
||||
--context="": The name of the kubeconfig context to use
|
||||
--insecure-skip-tls-verify[=false]: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
|
||||
--kubeconfig="": Path to the kubeconfig file to use for CLI requests.
|
||||
--log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
|
||||
--log-dir="": If non-empty, write log files in this directory
|
||||
--log-flush-frequency=5s: Maximum number of seconds between log flushes
|
||||
--logtostderr[=true]: log to standard error instead of files
|
||||
--match-server-version[=false]: Require server version to match client version
|
||||
--namespace="": If present, the namespace scope for this CLI request.
|
||||
--password="": Password for basic authentication to the API server.
|
||||
-s, --server="": The address and port of the Kubernetes API server
|
||||
--stderrthreshold=2: logs at or above this threshold go to stderr
|
||||
--token="": Bearer token for authentication to the API server.
|
||||
--user="": The name of the kubeconfig user to use
|
||||
--username="": Username for basic authentication to the API server.
|
||||
--v=0: log level for V logs
|
||||
--vmodule=: comma-separated list of pattern=N settings for file-filtered logging
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-09-25 11:07:41.758531939 +0000 UTC
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
[]()
|
||||
<!-- END MUNGE: GENERATED_ANALYTICS -->
|
@ -190,6 +190,7 @@ node-startup-grace-period
|
||||
node-status-update-frequency
|
||||
node-sync-period
|
||||
no-headers
|
||||
no-suggestions
|
||||
num-nodes
|
||||
oidc-ca-file
|
||||
oidc-client-id
|
||||
|
@ -172,6 +172,7 @@ Find more information at https://github.com/kubernetes/kubernetes.`,
|
||||
cmds.AddCommand(NewCmdClusterInfo(f, out))
|
||||
cmds.AddCommand(NewCmdApiVersions(f, out))
|
||||
cmds.AddCommand(NewCmdVersion(f, out))
|
||||
cmds.AddCommand(NewCmdExplain(f, out))
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
85
pkg/kubectl/cmd/explain.go
Normal file
85
pkg/kubectl/cmd/explain.go
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
const (
|
||||
explainExamples = `# Get the documentation of the resource and its fields
|
||||
$ kubectl explain pods
|
||||
|
||||
# Get the documentation of a specific field of a resource
|
||||
$ kubectl explain pods.spec.containers`
|
||||
|
||||
explainLong = `Documentation of resources.
|
||||
|
||||
Possible resource types include: pods (po), services (svc),
|
||||
replicationcontrollers (rc), nodes (no), events (ev), componentstatuses (cs),
|
||||
limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc),
|
||||
resourcequotas (quota), namespaces (ns) or endpoints (ep).`
|
||||
)
|
||||
|
||||
// NewCmdExplain returns a cobra command for swagger docs
|
||||
func NewCmdExplain(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "explain RESOURCE",
|
||||
Short: "Documentation of resources.",
|
||||
Long: explainLong,
|
||||
Example: explainExamples,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunExplain(f, out, cmd, args)
|
||||
cmdutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
cmd.Flags().Bool("recursive", false, "Print the fields of fields (Currently only 1 level deep)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunExplain executes the appropriate steps to print a model's documentation
|
||||
func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE")
|
||||
}
|
||||
|
||||
client, err := f.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recursive := cmdutil.GetFlagBool(cmd, "recursive")
|
||||
apiV := cmdutil.GetFlagString(cmd, "api-version")
|
||||
|
||||
swagSchema, err := kubectl.GetSwaggerSchema(apiV, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, _ := f.Object()
|
||||
inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return kubectl.PrintModelDescription(inModel, fieldsPath, out, swagSchema, recursive)
|
||||
}
|
260
pkg/kubectl/explain.go
Normal file
260
pkg/kubectl/explain.go
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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/swagger"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
)
|
||||
|
||||
var allModels = make(map[string]*swagger.NamedModel)
|
||||
var recursive = false // this is global for convenience, can become int for multiple levels
|
||||
|
||||
// GetSwaggerSchema returns the swagger spec from master
|
||||
func GetSwaggerSchema(apiVer string, kubeClient client.Interface) (*swagger.ApiDeclaration, error) {
|
||||
swaggerSchema, err := kubeClient.SwaggerSchema(apiVer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't read swagger schema from server: %v", err)
|
||||
}
|
||||
return swaggerSchema, nil
|
||||
}
|
||||
|
||||
// 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(expandResourceShortcut(inResource))
|
||||
return inResource, fieldsPath, nil
|
||||
}
|
||||
|
||||
// PrintModelDescription prints the description of a specific model or dot path
|
||||
func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swaggerSchema *swagger.ApiDeclaration, r bool) error {
|
||||
recursive = r // this is global for convenience
|
||||
apiVer := 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 resourse: %s doesn't exit", inModel)
|
||||
}
|
||||
|
||||
if len(fieldsPath) == 0 {
|
||||
return printTopLevelResourceInfo(w, pointedModel)
|
||||
}
|
||||
|
||||
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: %s doesn't exist", field)
|
||||
}
|
||||
}
|
||||
return printModelInfo(w, pointedModel, pointedModelAsProp)
|
||||
}
|
||||
|
||||
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) 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)
|
||||
}
|
||||
|
||||
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) error {
|
||||
fmt.Fprintf(w, "DESCRIPTION:\n%s\n", model.Model.Description)
|
||||
return printFields(w, model)
|
||||
}
|
||||
|
||||
func printFields(w io.Writer, model *swagger.NamedModel) error {
|
||||
fmt.Fprint(w, "\nFIELDS:\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
|
||||
}
|
Loading…
Reference in New Issue
Block a user