Add phase commandline option for interacting with phases

Instead of having a lot different arbitrary named commands
such as "cluster initinfra", "cluster control-plane", add
a generic phase command that would provide capabilities
for interacting with phases (e.g. getting list or applying
specific one).

Change-Id: I1ab797121aecbfd2348933dfd993c1a5974edaf9
Relates-To: #162
This commit is contained in:
Ruslan Aliev 2020-04-28 22:16:54 -05:00
parent 009794e408
commit 0bbc88b4a9
17 changed files with 508 additions and 2 deletions

76
cmd/phase/apply.go Normal file
View File

@ -0,0 +1,76 @@
/*
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 phase
import (
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/k8s/client"
"opendev.org/airship/airshipctl/pkg/phase/apply"
)
const (
applyLong = `
Apply specific phase to kubernetes cluster such as control-plane, workloads, initinfra
`
applyExample = `
# Apply initinfra phase to a cluster
airshipctl phase apply initinfra
`
)
// NewApplyCommand creates a command to apply phase to k8s cluster.
func NewApplyCommand(rootSettings *environment.AirshipCTLSettings, factory client.Factory) *cobra.Command {
i := apply.NewOptions(rootSettings)
applyCmd := &cobra.Command{
Use: "apply PHASE_NAME",
Short: "Apply phase to a cluster",
Long: applyLong[1:],
Args: cobra.ExactArgs(1),
Example: applyExample,
RunE: func(cmd *cobra.Command, args []string) error {
i.PhaseName = args[0]
client, err := factory(rootSettings)
if err != nil {
return err
}
i.Client = client
return i.Run()
},
}
addApplyFlags(i, applyCmd)
return applyCmd
}
func addApplyFlags(i *apply.Options, cmd *cobra.Command) {
flags := cmd.Flags()
flags.BoolVar(
&i.DryRun,
"dry-run",
false,
"don't deliver documents to the cluster, simulate the changes instead")
flags.BoolVar(
&i.Prune,
"prune",
false,
`if set to true, command will delete all kubernetes resources that are not`+
` defined in airship documents and have airshipit.org/deployed=apply label`)
}

47
cmd/phase/apply_test.go Normal file
View File

@ -0,0 +1,47 @@
/*
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"
"opendev.org/airship/airshipctl/cmd/phase"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/k8s/client"
"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
"opendev.org/airship/airshipctl/testutil"
)
func TestNewApplyCommand(t *testing.T) {
fakeRootSettings := &environment.AirshipCTLSettings{
AirshipConfigPath: "../../testdata/k8s/config.yaml",
KubeConfigPath: "../../testdata/k8s/kubeconfig.yaml",
}
fakeRootSettings.InitConfig()
testClientFactory := func(_ *environment.AirshipCTLSettings) (client.Interface, error) {
return fake.NewClient(), nil
}
tests := []*testutil.CmdTest{
{
Name: "phase-apply-cmd-with-help",
CmdLine: "--help",
Cmd: phase.NewApplyCommand(fakeRootSettings, testClientFactory),
},
}
for _, testcase := range tests {
testutil.RunTest(t, testcase)
}
}

42
cmd/phase/phase.go Normal file
View File

@ -0,0 +1,42 @@
/*
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
import (
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/k8s/client"
)
const (
clusterLong = `
This command provides capabilities for interacting with phases,
such as getting list and applying specific one.
`
)
// NewPhaseCommand creates a command for interacting with phases
func NewPhaseCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
phaseRootCmd := &cobra.Command{
Use: "phase",
Short: "Manage phases",
Long: clusterLong[1:],
}
phaseRootCmd.AddCommand(NewApplyCommand(rootSettings, client.DefaultClient))
return phaseRootCmd
}

42
cmd/phase/phase_test.go Normal file
View File

@ -0,0 +1,42 @@
/*
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"
"opendev.org/airship/airshipctl/cmd/phase"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/testutil"
)
func TestNewPhaseCommand(t *testing.T) {
fakeRootSettings := &environment.AirshipCTLSettings{
AirshipConfigPath: "../../testdata/k8s/config.yaml",
KubeConfigPath: "../../testdata/k8s/kubeconfig.yaml",
}
fakeRootSettings.InitConfig()
tests := []*testutil.CmdTest{
{
Name: "phase-cmd-with-help",
CmdLine: "--help",
Cmd: phase.NewPhaseCommand(fakeRootSettings),
},
}
for _, testcase := range tests {
testutil.RunTest(t, testcase)
}
}

View File

@ -0,0 +1,15 @@
Apply specific phase to kubernetes cluster such as control-plane, workloads, initinfra
Usage:
apply PHASE_NAME [flags]
Examples:
# Apply initinfra phase to a cluster
airshipctl phase apply initinfra
Flags:
--dry-run don't deliver documents to the cluster, simulate the changes instead
-h, --help help for apply
--prune if set to true, command will delete all kubernetes resources that are not defined in airship documents and have airshipit.org/deployed=apply label

View File

@ -0,0 +1,14 @@
This command provides capabilities for interacting with phases,
such as getting list and applying specific one.
Usage:
phase [command]
Available Commands:
apply Apply phase to a cluster
help Help about any command
Flags:
-h, --help help for phase
Use "phase [command] --help" for more information about a command.

View File

@ -27,6 +27,7 @@ import (
"opendev.org/airship/airshipctl/cmd/completion"
"opendev.org/airship/airshipctl/cmd/config"
"opendev.org/airship/airshipctl/cmd/document"
"opendev.org/airship/airshipctl/cmd/phase"
"opendev.org/airship/airshipctl/cmd/secret"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/log"
@ -71,6 +72,7 @@ func AddDefaultAirshipCTLCommands(cmd *cobra.Command, settings *environment.Airs
cmd.AddCommand(document.NewDocumentCommand(settings))
cmd.AddCommand(config.NewConfigCommand(settings))
cmd.AddCommand(secret.NewSecretCommand())
cmd.AddCommand(phase.NewPhaseCommand(settings))
return cmd
}

View File

@ -10,6 +10,7 @@ Available Commands:
config Manage the airshipctl config file
document Manage deployment documents
help Help about any command
phase Manage phases
secret Manage secrets
version Show the version number of airshipctl

View File

@ -61,7 +61,7 @@ func (infra *Infra) Deploy() error {
ao.SetDryRun(infra.DryRun)
// If prune is true, set selector for purning
if infra.Prune {
ao.SetPrune(document.InitInfraSelector)
ao.SetPrune(document.ApplyPhaseSelector + "initinfra")
}
globalConf := infra.RootSettings.Config

View File

@ -19,7 +19,7 @@ const (
BaseAirshipSelector = "airshipit.org"
EphemeralHostSelector = BaseAirshipSelector + "/ephemeral-node in (True, true)"
EphemeralUserDataSelector = BaseAirshipSelector + "/ephemeral-user-data in (True, true)"
InitInfraSelector = BaseAirshipSelector + "/phase = initinfra"
ApplyPhaseSelector = BaseAirshipSelector + "/phase = "
// Please note that by default every document in the manifest is to be deployed to kubernetes cluster.
// so this selector simply checks that deploy-k8s label is not equal to false or False (string)

76
pkg/phase/apply/apply.go Normal file
View File

@ -0,0 +1,76 @@
/*
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 apply
import (
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/k8s/client"
)
// Options is an abstraction used to apply the phase
type Options struct {
RootSettings *environment.AirshipCTLSettings
Client client.Interface
DryRun bool
Prune bool
PhaseName string
}
// NewOptions return instance of Options
func NewOptions(settings *environment.AirshipCTLSettings) *Options {
// At this point AirshipCTLSettings may not be fully initialized
applyOptions := &Options{RootSettings: settings}
return applyOptions
}
// Run apply subcommand logic
func (applyOptions *Options) Run() error {
kctl := applyOptions.Client.Kubectl()
ao, err := kctl.ApplyOptions()
if err != nil {
return err
}
ao.SetDryRun(applyOptions.DryRun)
// If prune is true, set selector for pruning
if applyOptions.Prune {
ao.SetPrune(document.ApplyPhaseSelector + applyOptions.PhaseName)
}
globalConf := applyOptions.RootSettings.Config
kustomizePath, err := globalConf.CurrentContextEntryPoint(applyOptions.PhaseName)
if err != nil {
return err
}
b, err := document.NewBundleByPath(kustomizePath)
if err != nil {
return err
}
// Returns all documents for this phase
docs, err := b.Select(document.NewDeployToK8sSelector())
if err != nil {
return err
}
if len(docs) == 0 {
return document.ErrDocNotFound{}
}
return kctl.Apply(docs, ao)
}

View File

@ -0,0 +1,105 @@
/*
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 apply_test
import (
"errors"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/k8s/client"
"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
"opendev.org/airship/airshipctl/pkg/k8s/kubectl"
"opendev.org/airship/airshipctl/pkg/phase/apply"
"opendev.org/airship/airshipctl/testutil/k8sutils"
)
const (
kubeconfigPath = "testdata/kubeconfig.yaml"
filenameRC = "testdata/primary/site/test-site/ephemeral/initinfra/replicationcontroller.yaml"
airshipConfigFile = "testdata/config.yaml"
)
var (
DynamicClientError = errors.New("DynamicClientError")
)
func TestDeploy(t *testing.T) {
rs := makeNewFakeRootSettings(t, kubeconfigPath, airshipConfigFile)
tf := k8sutils.NewFakeFactoryForRC(t, filenameRC)
defer tf.Cleanup()
ao := apply.NewOptions(rs)
ao.PhaseName = "initinfra"
ao.DryRun = true
kctl := kubectl.NewKubectl(tf)
tests := []struct {
theApplyOptions *apply.Options
client client.Interface
prune bool
expectedError error
}{
{
client: fake.NewClient(fake.WithKubectl(
kubectl.NewKubectl(k8sutils.
NewMockKubectlFactory().
WithDynamicClientByError(nil, DynamicClientError)))),
expectedError: DynamicClientError,
},
{
expectedError: nil,
prune: false,
client: fake.NewClient(fake.WithKubectl(kctl)),
},
{
expectedError: nil,
prune: true,
client: fake.NewClient(fake.WithKubectl(kctl)),
},
}
for _, test := range tests {
ao.Prune = test.prune
ao.Client = test.client
actualErr := ao.Run()
assert.Equal(t, test.expectedError, actualErr)
}
}
// makeNewFakeRootSettings takes kubeconfig path and directory path to fixture dir as argument.
func makeNewFakeRootSettings(t *testing.T, kp string, dir string) *environment.AirshipCTLSettings {
t.Helper()
akp, err := filepath.Abs(kp)
require.NoError(t, err)
adir, err := filepath.Abs(dir)
require.NoError(t, err)
settings := &environment.AirshipCTLSettings{
AirshipConfigPath: adir,
KubeConfigPath: akp,
}
settings.InitConfig()
return settings
}

44
pkg/phase/apply/testdata/config.yaml vendored Normal file
View File

@ -0,0 +1,44 @@
apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
container:
volume: /tmp/airship:/config
image: quay.io/airshipit/isogen:latest
containerRuntime: docker
builder:
userDataFileName: user-data
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
remoteDirect:
isoUrl: http://localhost:8099/debian-custom.iso
remoteType: redfish
clusters:
dummycluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummycluster_ephemeral
contexts:
dummy_cluster:
contextKubeconf: dummy_cluster
manifest: dummy_manifest
currentContext: dummy_cluster
kind: Config
manifests:
dummy_manifest:
primaryRepositoryName: primary
repositories:
primary:
auth:
sshKey: testdata/test-key.pem
type: ssh-key
checkout:
branch: ""
force: false
remoteRef: ""
tag: v1.0.1
url: http://dummy.url.com/primary.git
subPath: primary/site/test-site
targetPath: testdata
users:
dummy_user: {}

View File

@ -0,0 +1,19 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1Ea3lPVEUzTURNd09Wb1hEVEk1TURreU5qRTNNRE13T1Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUZyCkdxM0kyb2dZci81Y01Udy9Na1pORTNWQURzdEdyU240WjU2TDhPUGhMcUhDN2t1dno2dVpES3dCSGtGeTBNK2MKRXIzd2piUGE1aTV5NmkyMGtxSHBVMjdPZTA0dzBXV2s4N0RSZVlWaGNoZVJHRXoraWt3SndIcGRmMjJVemZNKwpkSDBzaUhuMVd6UnovYk4za3hMUzJlMnZ2U1Y3bmNubk1YRUd4OXV0MUY0NThHeWxxdmxXTUlWMzg5Q2didXFDCkcwcFdiMTBLM0RVZWdiT25Xa1FmSm5sTWRRVVZDUVdZZEZaaklrcWtkWi9hVTRobkNEV01oZXNWRnFNaDN3VVAKczhQay9BNWh1ZFFPbnFRNDVIWXZLdjZ5RjJWcDUyWExBRUx3NDJ4aVRKZlh0V1h4eHR6cU4wY1lyL2VxeS9XMQp1YVVGSW5xQjFVM0JFL1oxbmFrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFKUUVKQVBLSkFjVDVuK3dsWGJsdU9mS0J3c2gKZTI4R1c5R2QwM0N0NGF3RzhzMXE1ZHNua2tpZmVTUENHVFZ1SXF6UTZDNmJaSk9SMDMvVEl5ejh6NDJnaitDVApjWUZXZkltM2RKTnpRL08xWkdySXZZNWdtcWJtWDlpV0JaU24rRytEOGxubzd2aGMvY0tBRFR5OTMvVU92MThuCkdhMnIrRGJJcHcyTWVBVEl2elpxRS9RWlVSQ25DMmdjUFhTVzFqN2h4R3o1a3ZNcGVDZTdQYVUvdVFvblVHSWsKZ2t6ZzI4NHQvREhUUzc4N1V1SUg5cXBaV09yTFNMOGFBeUxQUHhWSXBteGZmbWRETE9TS2VUemRlTmxoSitUMwowQlBVaHBQTlJBNTNJN0hRQjhVUDR2elNONTkzZ1VFbVlFQ2Jic2RYSzB6ZVR6SDdWWHR2Zmd5WTVWWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://127.0.0.1:6443
name: dummycluster_ephemeral
contexts:
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
current-context: dummy_cluster
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQXhEdzk2RUY4SXN3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNU1qa3hOekF6TURsYUZ3MHlNREE1TWpneE56QXpNVEphTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXV6R0pZdlBaNkRvaTQyMUQKSzhXSmFaQ25OQWQycXo1cC8wNDJvRnpRUGJyQWd6RTJxWVZrek9MOHhBVmVSN1NONXdXb1RXRXlGOEVWN3JyLwo0K0hoSEdpcTVQbXF1SUZ5enpuNi9JWmM4alU5eEVmenZpa2NpckxmVTR2UlhKUXdWd2dBU05sMkFXQUloMmRECmRUcmpCQ2ZpS1dNSHlqMFJiSGFsc0J6T3BnVC9IVHYzR1F6blVRekZLdjJkajVWMU5rUy9ESGp5UlJKK0VMNlEKQlltR3NlZzVQNE5iQzllYnVpcG1NVEFxL0p1bU9vb2QrRmpMMm5acUw2Zkk2ZkJ0RjVPR2xwQ0IxWUo4ZnpDdApHUVFaN0hUSWJkYjJ0cDQzRlZPaHlRYlZjSHFUQTA0UEoxNSswV0F5bVVKVXo4WEE1NDRyL2J2NzRKY0pVUkZoCmFyWmlRd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMMmhIUmVibEl2VHJTMFNmUVg1RG9ueVVhNy84aTg1endVWApSd3dqdzFuS0U0NDJKbWZWRGZ5b0hRYUM4Ti9MQkxyUXM0U0lqU1JYdmFHU1dSQnRnT1RRV21Db1laMXdSbjdwCndDTXZQTERJdHNWWm90SEZpUFl2b1lHWFFUSXA3YlROMmg1OEJaaEZ3d25nWUovT04zeG1rd29IN1IxYmVxWEYKWHF1TTluekhESk41VlZub1lQR09yRHMwWlg1RnNxNGtWVU0wVExNQm9qN1ZIRDhmU0E5RjRYNU4yMldsZnNPMAo4aksrRFJDWTAyaHBrYTZQQ0pQS0lNOEJaMUFSMG9ZakZxT0plcXpPTjBqcnpYWHh4S2pHVFVUb1BldVA5dCtCCjJOMVA1TnI4a2oxM0lrend5Q1NZclFVN09ZM3ltZmJobHkrcXZxaFVFa014MlQ1SkpmQT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBdXpHSll2UFo2RG9pNDIxREs4V0phWkNuTkFkMnF6NXAvMDQyb0Z6UVBickFnekUyCnFZVmt6T0w4eEFWZVI3U041d1dvVFdFeUY4RVY3cnIvNCtIaEhHaXE1UG1xdUlGeXp6bjYvSVpjOGpVOXhFZnoKdmlrY2lyTGZVNHZSWEpRd1Z3Z0FTTmwyQVdBSWgyZERkVHJqQkNmaUtXTUh5ajBSYkhhbHNCek9wZ1QvSFR2MwpHUXpuVVF6Rkt2MmRqNVYxTmtTL0RIanlSUkorRUw2UUJZbUdzZWc1UDROYkM5ZWJ1aXBtTVRBcS9KdW1Pb29kCitGakwyblpxTDZmSTZmQnRGNU9HbHBDQjFZSjhmekN0R1FRWjdIVEliZGIydHA0M0ZWT2h5UWJWY0hxVEEwNFAKSjE1KzBXQXltVUpVejhYQTU0NHIvYnY3NEpjSlVSRmhhclppUXdJREFRQUJBb0lCQVFDU0pycjlaeVpiQ2dqegpSL3VKMFZEWCt2aVF4c01BTUZyUjJsOE1GV3NBeHk1SFA4Vk4xYmc5djN0YUVGYnI1U3hsa3lVMFJRNjNQU25DCm1uM3ZqZ3dVQWlScllnTEl5MGk0UXF5VFBOU1V4cnpTNHRxTFBjM3EvSDBnM2FrNGZ2cSsrS0JBUUlqQnloamUKbnVFc1JpMjRzT3NESlM2UDE5NGlzUC9yNEpIM1M5bFZGbkVuOGxUR2c0M1kvMFZoMXl0cnkvdDljWjR5ZUNpNwpjMHFEaTZZcXJZaFZhSW9RRW1VQjdsbHRFZkZzb3l4VDR6RTE5U3pVbkRoMmxjYTF1TzhqcmI4d2xHTzBoQ2JyClB1R1l2WFFQa3Q0VlNmalhvdGJ3d2lBNFRCVERCRzU1bHp6MmNKeS9zSS8zSHlYbEMxcTdXUmRuQVhhZ1F0VzkKOE9DZGRkb0JBb0dCQU5NcUNtSW94REtyckhZZFRxT1M1ZFN4cVMxL0NUN3ZYZ0pScXBqd2Y4WHA2WHo0KzIvTAozVXFaVDBEL3dGTkZkc1Z4eFYxMnNYMUdwMHFWZVlKRld5OVlCaHVSWGpTZ0ZEWldSY1Z1Y01sNVpPTmJsbmZGCjVKQ0xnNXFMZ1g5VTNSRnJrR3A0R241UDQxamg4TnhKVlhzZG5xWE9xNTFUK1RRT1UzdkpGQjc1QW9HQkFPTHcKalp1cnZtVkZyTHdaVGgvRDNpWll5SVV0ZUljZ2NKLzlzbTh6L0pPRmRIbFd4dGRHUFVzYVd1MnBTNEhvckFtbgpqTm4vSTluUXd3enZ3MWUzVVFPbUhMRjVBczk4VU5hbk5TQ0xNMW1yaXZHRXJ1VHFnTDM1bU41eFZPdTUxQU5JCm4yNkFtODBJT2JDeEtLa0R0ZXJSaFhHd3g5c1pONVJCbG9VRThZNGJBb0dBQ3ZsdVhMZWRxcng5VkE0bDNoNXUKVDJXRVUxYjgxZ1orcmtRc1I1S0lNWEw4cllBTElUNUpHKzFuendyN3BkaEFXZmFWdVV2SDRhamdYT0h6MUs5aQpFODNSVTNGMG9ldUg0V01PY1RwU0prWm0xZUlXcWRiaEVCb1FGdUlWTXRib1BsV0d4ZUhFRHJoOEtreGp4aThSCmdEcUQyajRwY1IzQ0g5QjJ5a0lqQjVFQ2dZRUExc0xXLys2enE1c1lNSm14K1JXZThhTXJmL3pjQnVTSU1LQWgKY0dNK0wwMG9RSHdDaUU4TVNqcVN1ajV3R214YUFuanhMb3ZwSFlRV1VmUEVaUW95UE1YQ2VhRVBLOU4xbk8xMwp0V2lHRytIZkIxaU5PazFCc0lhNFNDbndOM1FRVTFzeXBaeEgxT3hueS9LYmkvYmEvWEZ5VzNqMGFUK2YvVWxrCmJGV1ZVdWtDZ1lFQTBaMmRTTFlmTjV5eFNtYk5xMWVqZXdWd1BjRzQxR2hQclNUZEJxdHFac1doWGE3aDdLTWEKeHdvamh5SXpnTXNyK2tXODdlajhDQ2h0d21sQ1p5QU92QmdOZytncnJ1cEZLM3FOSkpKeU9YREdHckdpbzZmTQp5aXB3Q2tZVGVxRThpZ1J6UkI5QkdFUGY4eVpjMUtwdmZhUDVhM0lRZmxiV0czbGpUemNNZVZjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

View File

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

View File

@ -0,0 +1,19 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: test-rc
namespace: test
labels:
airshipit.org/phase: "initinfra"
spec:
replicas: 1
template:
metadata:
labels:
name: test-rc
spec:
containers:
- name: test-rc
image: nginx
ports:
- containerPort: 80

View File

@ -0,0 +1,2 @@
resources:
- initinfra