Merge "Implement document rendering sub-command"

This commit is contained in:
Zuul 2020-05-27 21:09:34 +00:00 committed by Gerrit Code Review
commit 88542a66b5
25 changed files with 587 additions and 53 deletions

View File

@ -35,7 +35,6 @@ func NewDocumentCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Com
} }
documentRootCmd.AddCommand(NewPullCommand(rootSettings)) documentRootCmd.AddCommand(NewPullCommand(rootSettings))
documentRootCmd.AddCommand(NewRenderCommand(rootSettings))
documentRootCmd.AddCommand(NewPluginCommand(rootSettings)) documentRootCmd.AddCommand(NewPluginCommand(rootSettings))
return documentRootCmd return documentRootCmd

View File

@ -28,11 +28,6 @@ func TestDocument(t *testing.T) {
CmdLine: "-h", CmdLine: "-h",
Cmd: document.NewDocumentCommand(nil), Cmd: document.NewDocumentCommand(nil),
}, },
{
Name: "document-render-with-help",
CmdLine: "-h",
Cmd: document.NewRenderCommand(nil),
},
{ {
Name: "document-plugin-with-help", Name: "document-plugin-with-help",
CmdLine: "-h", CmdLine: "-h",

View File

@ -1,12 +0,0 @@
Render documents from model
Usage:
render [flags]
Flags:
-a, --annotation stringArray filter documents by Annotations
-g, --apiversion stringArray filter documents by API version
-f, --filter string logical expression for document filtering
-h, --help help for render
-k, --kind stringArray filter documents by Kinds
-l, --label stringArray filter documents by Labels

View File

@ -7,7 +7,6 @@ Available Commands:
help Help about any command help Help about any command
plugin Run as a kustomize exec plugin plugin Run as a kustomize exec plugin
pull Pulls documents from remote git repository pull Pulls documents from remote git repository
render Render documents from model
Flags: Flags:
-h, --help help for document -h, --help help for document

View File

@ -44,6 +44,7 @@ func NewPhaseCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Comman
} }
phaseRootCmd.AddCommand(NewApplyCommand(rootSettings, client.DefaultClient)) phaseRootCmd.AddCommand(NewApplyCommand(rootSettings, client.DefaultClient))
phaseRootCmd.AddCommand(NewRenderCommand(rootSettings))
return phaseRootCmd return phaseRootCmd
} }

View File

@ -12,24 +12,41 @@
limitations under the License. limitations under the License.
*/ */
package document package phase
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/document/render"
"opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/errors" "opendev.org/airship/airshipctl/pkg/phase/render"
)
const (
renderExample = `
# Get all 'initinfra' phase documents containing labels "app=helm" and
# "service=tiller"
airshipctl phase render initinfra -l app=helm,service=tiller
# Get all documents containing labels "app=helm" and "service=tiller"
# and kind 'Deployment'
airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment
`
) )
// NewRenderCommand create a new command for document rendering // NewRenderCommand create a new command for document rendering
func NewRenderCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command { func NewRenderCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
renderSettings := &render.Settings{AirshipCTLSettings: rootSettings} renderSettings := &render.Settings{AirshipCTLSettings: rootSettings}
renderCmd := &cobra.Command{ renderCmd := &cobra.Command{
Use: "render", Use: "render PHASE_NAME",
Short: "Render documents from model", Short: "Render phase documents from model",
Example: renderExample,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return errors.ErrNotImplemented{} path, err := renderSettings.Config.CurrentContextEntryPoint(args[0])
if err != nil {
return err
}
return renderSettings.Render(path, cmd.OutOrStdout())
}, },
} }
@ -41,38 +58,31 @@ func NewRenderCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Comma
func addRenderFlags(settings *render.Settings, cmd *cobra.Command) { func addRenderFlags(settings *render.Settings, cmd *cobra.Command) {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringArrayVarP( flags.StringVarP(
&settings.Label, &settings.Label,
"label", "label",
"l", "l",
nil, "",
"filter documents by Labels") "filter documents by Labels")
flags.StringArrayVarP( flags.StringVarP(
&settings.Annotation, &settings.Annotation,
"annotation", "annotation",
"a", "a",
nil, "",
"filter documents by Annotations") "filter documents by Annotations")
flags.StringArrayVarP( flags.StringVarP(
&settings.GroupVersion, &settings.APIVersion,
"apiversion", "apiversion",
"g", "g",
nil, "",
"filter documents by API version") "filter documents by API version")
flags.StringArrayVarP( flags.StringVarP(
&settings.Kind, &settings.Kind,
"kind", "kind",
"k", "k",
nil,
"filter documents by Kinds")
flags.StringVarP(
&settings.RawFilter,
"filter",
"f",
"", "",
"logical expression for document filtering") "filter documents by Kinds")
} }

61
cmd/phase/render_test.go Normal file
View File

@ -0,0 +1,61 @@
/*
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
https://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 phase_test
import (
"testing"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/cmd/phase"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/testutil"
)
func TestRender(t *testing.T) {
cfg, cleanupCfg := testutil.InitConfig(t)
defer cleanupCfg(t)
cfg.CurrentContext = "def_ephemeral"
cfg.Manifests["test"] = &config.Manifest{
TargetPath: "testdata",
PrimaryRepositoryName: "testRepo",
Repositories: map[string]*config.Repository{
"testRepo": {
URLString: "http://localhost",
},
},
}
ctx, err := cfg.GetContext("def_ephemeral")
require.NoError(t, err)
ctx.Manifest = "test"
settings := &environment.AirshipCTLSettings{Config: cfg}
tests := []*testutil.CmdTest{
{
Name: "render-with-help",
CmdLine: "-h",
Cmd: phase.NewRenderCommand(nil),
},
{
Name: "render-with-multiple-labels",
CmdLine: "initinfra -l app=helm,name=tiller",
Cmd: phase.NewRenderCommand(settings),
},
}
for _, tt := range tests {
testutil.RunTest(t, tt)
}
}

View File

@ -7,6 +7,7 @@ Usage:
Available Commands: Available Commands:
apply Apply phase to a cluster apply Apply phase to a cluster
help Help about any command help Help about any command
render Render phase documents from model
Flags: Flags:
-h, --help help for phase -h, --help help for phase

View File

@ -0,0 +1,22 @@
Render phase documents from model
Usage:
render PHASE_NAME [flags]
Examples:
# Get all 'initinfra' phase documents containing labels "app=helm" and
# "service=tiller"
airshipctl phase render initinfra -l app=helm,service=tiller
# Get all documents containing labels "app=helm" and "service=tiller"
# and kind 'Deployment'
airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment
Flags:
-a, --annotation string filter documents by Annotations
-g, --apiversion string filter documents by API version
-h, --help help for render
-k, --kind string filter documents by Kinds
-l, --label string filter documents by Labels

View File

@ -0,0 +1,52 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
airshipit.org/clustertype: ephemeral
creationTimestamp: null
labels:
app: helm
name: tiller
name: tiller-deploy
namespace: kube-system
spec:
replicas: 1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: helm
name: tiller
spec:
automountServiceAccountToken: true
containers:
- env:
- name: TILLER_NAMESPACE
value: kube-system
- name: TILLER_HISTORY_MAX
value: "0"
image: gcr.io/kubernetes-helm/tiller:v2.12.3
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /liveness
port: 44135
initialDelaySeconds: 1
timeoutSeconds: 1
name: tiller
ports:
- containerPort: 44134
name: tiller
- containerPort: 44135
name: http
readinessProbe:
httpGet:
path: /readiness
port: 44135
initialDelaySeconds: 1
timeoutSeconds: 1
resources: {}
status: {}
...

View File

@ -0,0 +1,2 @@
resources:
- tiller.yaml

View File

@ -0,0 +1,74 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
airshipit.org/clustertype: ephemeral
creationTimestamp: null
labels:
app: helm
name: tiller
name: tiller-deploy
namespace: kube-system
spec:
replicas: 1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: helm
name: tiller
spec:
automountServiceAccountToken: true
containers:
- env:
- name: TILLER_NAMESPACE
value: kube-system
- name: TILLER_HISTORY_MAX
value: "0"
image: gcr.io/kubernetes-helm/tiller:v2.12.3
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /liveness
port: 44135
initialDelaySeconds: 1
timeoutSeconds: 1
name: tiller
ports:
- containerPort: 44134
name: tiller
- containerPort: 44135
name: http
readinessProbe:
httpGet:
path: /readiness
port: 44135
initialDelaySeconds: 1
timeoutSeconds: 1
resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
annotations:
airshipit.org/clustertype: ephemeral
creationTimestamp: null
labels:
app: helm
name: tiller-deploy
namespace: kube-system
spec:
ports:
- name: tiller
port: 44134
targetPort: tiller
selector:
app: helm
name: tiller
type: ClusterIP
status:
loadBalancer: {}
...

View File

@ -26,5 +26,4 @@ such as getting status and deploying initial infrastructure.
* [airshipctl](airshipctl.md) - A unified entrypoint to various airship components * [airshipctl](airshipctl.md) - A unified entrypoint to various airship components
* [airshipctl cluster init](airshipctl_cluster_init.md) - Deploy cluster-api provider components * [airshipctl cluster init](airshipctl_cluster_init.md) - Deploy cluster-api provider components
* [airshipctl cluster initinfra](airshipctl_cluster_initinfra.md) - Deploy initinfra components to cluster

View File

@ -25,5 +25,4 @@ Manage deployment documents
* [airshipctl](airshipctl.md) - A unified entrypoint to various airship components * [airshipctl](airshipctl.md) - A unified entrypoint to various airship components
* [airshipctl document plugin](airshipctl_document_plugin.md) - Run as a kustomize exec plugin * [airshipctl document plugin](airshipctl_document_plugin.md) - Run as a kustomize exec plugin
* [airshipctl document pull](airshipctl_document_pull.md) - Pulls documents from remote git repository * [airshipctl document pull](airshipctl_document_pull.md) - Pulls documents from remote git repository
* [airshipctl document render](airshipctl_document_render.md) - Render documents from model

View File

@ -26,4 +26,5 @@ such as getting list and applying specific one.
* [airshipctl](airshipctl.md) - A unified entrypoint to various airship components * [airshipctl](airshipctl.md) - A unified entrypoint to various airship components
* [airshipctl phase apply](airshipctl_phase_apply.md) - Apply phase to a cluster * [airshipctl phase apply](airshipctl_phase_apply.md) - Apply phase to a cluster
* [airshipctl phase render](airshipctl_phase_render.md) - Render phase documents from model

View File

@ -0,0 +1,48 @@
## airshipctl phase render
Render phase documents from model
### Synopsis
Render phase documents from model
```
airshipctl phase render PHASE_NAME [flags]
```
### Examples
```
#Get all 'initinfra' phase documents containing labels "app=helm" and
#"service=tiller"
airshipctl phase render initinfra -l app=helm,service=tiller
#Get all documents containing labels "app=helm" and "service=tiller"
#and kind 'Deployment'
airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment
```
### Options
```
-a, --annotation string filter documents by Annotations
-g, --apiversion string filter documents by API version
-h, --help help for render
-k, --kind string filter documents by Kinds
-l, --label string filter documents by Labels
```
### Options inherited from parent commands
```
--airshipconf string Path to file for airshipctl configuration. (default "$HOME/.airship/config")
--debug enable verbose output
--kubeconfig string Path to kubeconfig associated with airshipctl configuration. (default "$HOME/.airship/kubeconfig")
```
### SEE ALSO
* [airshipctl phase](airshipctl_phase.md) - Manage phases

View File

@ -0,0 +1,49 @@
/*
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
https://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 render
import (
"io"
"strings"
"opendev.org/airship/airshipctl/pkg/document"
)
// Render prints out filtered documents
func (s *Settings) Render(path string, out io.Writer) error {
if err := s.Config.EnsureComplete(); err != nil {
return err
}
docBundle, err := document.NewBundleByPath(path)
if err != nil {
return err
}
groupVersion := strings.Split(s.APIVersion, "/")
group := ""
version := groupVersion[0]
if len(groupVersion) > 1 {
group = groupVersion[0]
version = strings.Join(groupVersion[1:], "/")
}
sel := document.NewSelector().ByLabel(s.Label).ByAnnotation(s.Annotation).ByGvk(group, version, s.Kind)
filteredBundle, err := docBundle.SelectBundle(sel)
if err != nil {
return err
}
return filteredBundle.Write(out)
}

View File

@ -0,0 +1,94 @@
/*
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
https://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 render_test
import (
"bytes"
"fmt"
"io/ioutil"
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/phase/render"
"opendev.org/airship/airshipctl/testutil"
)
func TestRender(t *testing.T) {
rs := &environment.AirshipCTLSettings{Config: testutil.DummyConfig()}
fixturePath := "testdata/phase"
tests := []struct {
name string
settings *render.Settings
expResFile string
expErr error
}{
{
name: "No Filters",
settings: &render.Settings{AirshipCTLSettings: rs},
expResFile: "noFilter.yaml",
expErr: nil,
},
{
name: "All Filters",
settings: &render.Settings{
AirshipCTLSettings: rs,
Label: "airshipit.org/deploy-k8s=false",
Annotation: "airshipit.org/clustertype=ephemeral",
APIVersion: "metal3.io/v1alpha1",
Kind: "BareMetalHost",
},
expResFile: "allFilters.yaml",
expErr: nil,
},
{
name: "Multiple Labels",
settings: &render.Settings{
AirshipCTLSettings: rs,
Label: "airshipit.org/deploy-k8s=false, airshipit.org/ephemeral-node=true",
},
expResFile: "multiLabels.yaml",
expErr: nil,
},
{
name: "Malformed Label",
settings: &render.Settings{
AirshipCTLSettings: rs,
Label: "app=(",
},
expResFile: "",
expErr: fmt.Errorf("unable to parse requirement: found '(', expected: identifier"),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var expectedOut []byte
var err error
if tt.expResFile != "" {
expectedOut, err = ioutil.ReadFile(path.Join("testdata", "expected", tt.expResFile))
require.NoError(t, err)
}
out := &bytes.Buffer{}
err = tt.settings.Render(fixturePath, out)
assert.Equal(t, tt.expErr, err)
assert.Equal(t, expectedOut, out.Bytes())
})
}
}

View File

@ -0,0 +1,23 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
annotations:
airshipit.org/clustertype: ephemeral
labels:
airshipit.org/deploy-k8s: "false"
airshipit.org/ephemeral-node: "true"
name: node02
spec:
bmc:
address: redfish+https://localhost:8443/redfish/v1/Systems/air-ephemeral
credentialsName: node02-bmc-secret
bootMACAddress: 00:3b:8b:0c:ec:8b
networkData:
name: node02-network-data
namespace: default
online: true
status:
provisioning:
state: externally provisioned
...

View File

@ -0,0 +1,23 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
annotations:
airshipit.org/clustertype: ephemeral
labels:
airshipit.org/deploy-k8s: "false"
airshipit.org/ephemeral-node: "true"
name: node02
spec:
bmc:
address: redfish+https://localhost:8443/redfish/v1/Systems/air-ephemeral
credentialsName: node02-bmc-secret
bootMACAddress: 00:3b:8b:0c:ec:8b
networkData:
name: node02-network-data
namespace: default
online: true
status:
provisioning:
state: externally provisioned
...

View File

@ -0,0 +1,42 @@
---
apiVersion: v1
data:
password: cGFzc3dvcmQ=
username: dXNlcm5hbWU=
kind: Secret
metadata:
name: node02-bmc-secret
type: Opaque
...
---
apiVersion: v1
kind: Secret
metadata:
name: node02-network-data
stringData:
networkData: someData
type: Opaque
...
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
annotations:
airshipit.org/clustertype: ephemeral
labels:
airshipit.org/deploy-k8s: "false"
airshipit.org/ephemeral-node: "true"
name: node02
spec:
bmc:
address: redfish+https://localhost:8443/redfish/v1/Systems/air-ephemeral
credentialsName: node02-bmc-secret
bootMACAddress: 00:3b:8b:0c:ec:8b
networkData:
name: node02-network-data
namespace: default
online: true
status:
provisioning:
state: externally provisioned
...

View File

@ -0,0 +1,14 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
annotations:
airshipit.org/clustertype: target
name: master-1
spec:
bmc:
address: ipmi://192.168.111.2:6230
credentialsName: master-1-bmc-secret
bootMACAddress: 01:3b:8b:0c:ec:8b
online: true
...

View File

@ -0,0 +1,38 @@
apiVersion: v1
data:
password: cGFzc3dvcmQ=
username: dXNlcm5hbWU=
kind: Secret
metadata:
name: node02-bmc-secret
type: Opaque
---
apiVersion: v1
stringData:
networkData: someData
kind: Secret
metadata:
name: node02-network-data
type: Opaque
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/deploy-k8s: "false"
airshipit.org/ephemeral-node: "true"
annotations:
airshipit.org/clustertype: ephemeral
name: node02
spec:
bmc:
address: redfish+https://localhost:8443/redfish/v1/Systems/air-ephemeral
credentialsName: node02-bmc-secret
bootMACAddress: 00:3b:8b:0c:ec:8b
networkData:
name: node02-network-data
namespace: default
online: true
status:
provisioning:
state: externally provisioned

View File

@ -0,0 +1,2 @@
resources:
- baremetal.yaml

View File

@ -21,14 +21,12 @@ import (
// Settings for document rendering // Settings for document rendering
type Settings struct { type Settings struct {
*environment.AirshipCTLSettings *environment.AirshipCTLSettings
// Label filter documents by label string // Label filters documents by label string
Label []string Label string
// Annotation filter documents by annotation string // Annotation filters documents by annotation string
Annotation []string Annotation string
// GroupVersion filter documents by API version // APIVersion filters documents by API group and version
GroupVersion []string APIVersion string
// Kind filter documents by document kind // Kind filters documents by document kind
Kind []string Kind string
// RawFilter contains logical expression to filter documents
RawFilter string
} }