Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gaelg/mark spec unsafe #1

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3af4acb
Add metrics and alerts tutorial to the docs (#6341)
assafad Mar 27, 2023
82b28e8
Use digest instead of tags in scorecard images (#6393)
tonyskapunk Apr 13, 2023
af14062
Update release docs with more details (#6394)
oceanc80 Apr 20, 2023
49b7f00
stop building "scorecard-storage", "scorecard-untar" images on releas…
acornett21 May 16, 2023
3159ef2
Replace non-existing manifests and prevent them from changing in the …
tkrishtop May 16, 2023
25f608b
updating molecule links (#6413) (#6414)
ShivangiM May 16, 2023
5a1acbf
enable ginkgolinter and fix findings (#6421)
nunnatsa May 18, 2023
6a6edfd
Fix 6323: compress the bundle configMap (#6408)
nunnatsa May 18, 2023
f7d90c4
ignore github access URI in htmlproofer (#6442)
grokspawn May 22, 2023
0df210f
Use GHA for python (#6444)
tonyskapunk May 23, 2023
aeba6ad
Update kuttl to v0.15.0 in the scorecard-test-kuttl image (#6401)
andreasgerstmayr May 23, 2023
5347d93
Update ubi to ubi-micro:8.7 (#6423)
oceanc80 May 23, 2023
1f711f5
state default reconcilePeriod value in ansible docs (#6449)
jberkhahn May 30, 2023
24d3812
images: Bump golang base image to 1.19 for ansible-operator images (#…
pit1sIBM May 30, 2023
46c8c4e
Bumped the Golang version to 1.19 in pre-requisite documentation (#6386)
ukumar009 May 30, 2023
0a203b7
add blurb about recorder to golang tutorial and sample (#6384)
jberkhahn May 30, 2023
78c5643
Release v1.29.0 (#6454)
everettraven May 31, 2023
cbb568e
ansible: mark spec as unsafe if required
gaelgatelement Mar 23, 2023
decec6d
ansible: add changelog about markUnsafe bugfix
gaelgatelement Mar 23, 2023
a226d81
markUnsafe: fix empty array converted to nil
gaelgatelement Mar 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
environment: deploy
strategy:
matrix:
id: ["operator-sdk", "helm-operator", "scorecard-test", "ansible-operator", "ansible-operator-2.11-preview", "scorecard-storage", "scorecard-untar"]
id: ["operator-sdk", "helm-operator", "scorecard-test", "ansible-operator", "ansible-operator-2.11-preview"]
steps:

- name: set up qemu
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/test-ansible.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ jobs:
with:
fetch-depth: 0
- run: sudo rm -rf /usr/local/bin/kustomize
- run: |
- uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: Run test e2e ansible molecule
run: |
env
export PATH=/opt/python/3.8.12/bin:${PATH}
sudo apt-get install python3 python3-pip
sudo pip3 install --upgrade setuptools pip
sudo pip3 install ansible~=2.9.13
pip3 install --user --upgrade setuptools pip
pip3 install --user ansible~=2.9.13
make test-e2e-ansible-molecule
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ linters:
- nakedret
- misspell
- ineffassign
- ginkgolinter
- goconst
- goimports
- errcheck
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ SHELL = /bin/bash
# This value must be updated to the release tag of the most recent release, a change that must
# occur in the release commit. IMAGE_VERSION will be removed once each subproject that uses this
# version is moved to a separate repo and release process.
export IMAGE_VERSION = v1.28.0
export IMAGE_VERSION = v1.29.0
# Build-time variables to inject into binaries
export SIMPLE_VERSION = $(shell (test "$(shell git describe --tags)" = "$(shell git describe --tags --abbrev=0)" && echo $(shell git describe --tags)) || echo $(shell git describe --tags --abbrev=0)+git)
export GIT_VERSION = $(shell git describe --dirty --tags --always)
Expand Down Expand Up @@ -92,7 +92,7 @@ build/scorecard-test build/scorecard-test-kuttl build/custom-scorecard-tests:

# Convenience wrapper for building all remotely hosted images.
.PHONY: image-build
IMAGE_TARGET_LIST = operator-sdk helm-operator ansible-operator ansible-operator-2.11-preview scorecard-test scorecard-test-kuttl scorecard-untar scorecard-storage
IMAGE_TARGET_LIST = operator-sdk helm-operator ansible-operator ansible-operator-2.11-preview scorecard-test scorecard-test-kuttl
image-build: $(foreach i,$(IMAGE_TARGET_LIST),image/$(i)) ## Build all images.

# Convenience wrapper for building dependency base images.
Expand Down
25 changes: 25 additions & 0 deletions changelog/fragments/10-mark-unsafe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# entries is a list of entries to include in
# release notes and/or the migration guide
entries:
- description: >
markUnsafe now correctly marks as unsafe the spec extra variable.

# kind is one of:
# - addition
# - change
# - deprecation
# - removal
# - bugfix
kind: "bugfix"

# Is this a breaking change?
breaking: false

# NOTE: ONLY USE `pull_request_override` WHEN ADDING THIS
# FILE FOR A PREVIOUSLY MERGED PULL_REQUEST!
#
# The generator auto-detects the PR number from the commit
# message in which this file was originally added.
#
# What is the pull request number (without the "#")?
# pull_request_override: 0
13 changes: 13 additions & 0 deletions changelog/generated/v1.29.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## v1.29.0

### Changes

- (scorecard): Update kuttl to v0.15.0 in the scorecard-test-kuttl image. ([#6401](https://github.com/operator-framework/operator-sdk/pull/6401))
- (ansible/v1): Bump the golang base image version in the ansible-operator Dockerfiles from 1.18 to 1.19. ([#6398](https://github.com/operator-framework/operator-sdk/pull/6398))
- (operator-sdk run bundle): Compress the bundle content, to avoid the configMap exceed max length error. The error will look like this:
`... ConfigMap ... is invalid: []: Too long: must have at most 1048576 bytes`.
Fixes issue [#6323](https://github.com/operator-framework/operator-sdk/issues/6323). ([#6408](https://github.com/operator-framework/operator-sdk/pull/6408))

### Bug Fixes

- (docs): Update the go version in the developer guide. The documentation wasn't updated when the go version was bumped to v1.19. ([#6101](https://github.com/operator-framework/operator-sdk/pull/6101))
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/onsi/ginkgo/v2 v2.7.0
github.com/onsi/gomega v1.24.2
github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230307164205-6e30bde28688
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230413193425-4632388adc61
github.com/operator-framework/java-operator-plugins v0.7.1-0.20230306190439-0eed476d2b75
github.com/operator-framework/operator-lib v0.11.1-0.20230306195046-28cadc6b6055
github.com/operator-framework/operator-manifest-tools v0.2.3-0.20230227155221-caa8b9e1ab12
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,8 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 h1:d/Pnr19TnmIq3zQ6ebewC+5jt5zqYbRkvYd37YZENQY=
github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42/go.mod h1:l/cuwtPxkVUY7fzYgdust2m9tlmb8I4pOvbsUufRb24=
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230307164205-6e30bde28688 h1:cT4lGfZjYupUkxzieyfC/7RadF7Vk1FybgWCnViutnM=
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230307164205-6e30bde28688/go.mod h1:QpVyiSOKGbWADyNRl7LvMlRuuMGrWXJQdEYyHPQWMUg=
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230413193425-4632388adc61 h1:FPO2hS4HNIU2pzWeX2KusKxqDFeGIURRMkxRtn/i570=
github.com/operator-framework/helm-operator-plugins v0.0.12-0.20230413193425-4632388adc61/go.mod h1:QpVyiSOKGbWADyNRl7LvMlRuuMGrWXJQdEYyHPQWMUg=
github.com/operator-framework/java-operator-plugins v0.7.1-0.20230306190439-0eed476d2b75 h1:mjMid39qs1lEXpIldVmj7sa1wtuZvYge8oHkT0qOY0Y=
github.com/operator-framework/java-operator-plugins v0.7.1-0.20230306190439-0eed476d2b75/go.mod h1:oQTt35EEUrDY8ca/kRWYz5omWsVhk9Sj78vKlHFqxjM=
github.com/operator-framework/operator-lib v0.11.1-0.20230306195046-28cadc6b6055 h1:G9N8wEf9qDZ/4Fj5cbIejKUoFOYta0v72Yg8tPAdvc0=
Expand Down
2 changes: 1 addition & 1 deletion hack/check-links.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ docker run --rm -v sdk-html:/target klakegg/html-proofer:3.18.8 /target \
--http-status-ignore 429 \
--allow_hash_href \
--typhoeus '{"followlocation":true,"connecttimeout":600,"timeout":600}' \
--url-ignore "/github.com\/operator-framework\/operator-sdk\/edit\/master\//,https://docs.github.com/en/get-started/quickstart/fork-a-repo"
--url-ignore "/github.com\/operator-framework\/operator-sdk\/edit\/master\//,https://docs.github.com/en/get-started/quickstart/fork-a-repo,https://github.com/operator-framework/operator-sdk/settings/access"
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ func (mh *Memcached) Run() {
mh.uncommentManifestsKustomizationv3()
}

mh.customizingMain()

mh.implementingE2ETests()

cmd := exec.Command("go", "mod", "tidy")
Expand Down Expand Up @@ -480,7 +482,7 @@ func (mh *Memcached) implementingMonitoring() {
mh.customizingController()

log.Infof("customizing Main")
mh.customizingMain()
mh.customizingMainMonitoring()

log.Infof("customizing Dockerfile")
mh.customizingDockerfile()
Expand Down Expand Up @@ -784,10 +786,26 @@ func (mh *Memcached) customizingController() {
pkg.CheckError("adding metric incrementation", err)
}

// customizingMain will customize main.go to register metrics
// customizingMain will add comments to main
func (mh *Memcached) customizingMain() {
var mainPath string

if mh.isV3() {
mainPath = filepath.Join(mh.ctx.Dir, "main.go")
} else {
mainPath = filepath.Join(mh.ctx.Dir, "cmd", "main.go")
}

err := kbutil.InsertCode(mainPath,
"Scheme: mgr.GetScheme(),",
mainRecorderFragment)
pkg.CheckError("adding recorder fragment", err)
}

// customizingMainMonitoring will customize main.go to register metrics
func (mh *Memcached) customizingMainMonitoring() {
var mainPath string

marker := "\"github.com/example/memcached-operator/"
if mh.isV3() {
mainPath = filepath.Join(mh.ctx.Dir, "main.go")
Expand Down Expand Up @@ -1385,6 +1403,10 @@ const controllerPrometheusRuleFragment = `
memcached := &cachev1alpha1.Memcached{}
err = r.Get(ctx, req.NamespacedName, memcached)`

const mainRecorderFragment = `
// Add a Recorder to the reconciler.
// This allows the operator author to emit events during reconcilliation.`

const monitoringv1ImportFragment = `

monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
Expand Down
2 changes: 1 addition & 1 deletion images/ansible-operator-2.11-preview/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM --platform=$BUILDPLATFORM golang:1.18 as builder
FROM --platform=$BUILDPLATFORM golang:1.19 as builder
ARG TARGETARCH

WORKDIR /workspace
Expand Down
2 changes: 1 addition & 1 deletion images/ansible-operator/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM --platform=$BUILDPLATFORM golang:1.18 as builder
FROM --platform=$BUILDPLATFORM golang:1.19 as builder
ARG TARGETARCH

WORKDIR /workspace
Expand Down
6 changes: 4 additions & 2 deletions images/scorecard-test-kuttl/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ COPY . .
RUN GOOS=linux GOARCH=$TARGETARCH make build/scorecard-test-kuttl

# Final image.
#FROM kudobuilder/kuttl@sha256:924a709a1d2c6bede8815415ea5d5be640b506ec5aeaddc68acb443ae8ee7926
FROM kudobuilder/kuttl:v0.12.1
#FROM kudobuilder/kuttl@sha256:8d4dad161521450db95f88fe0e62487cc6587c5818df2a4e750fb9e54c082170
FROM kudobuilder/kuttl:v0.15.0

ENV HOME=/opt/scorecard-test-kuttl \
USER_NAME=scorecard-test-kuttl \
Expand All @@ -29,6 +29,8 @@ ENV HOME=/opt/scorecard-test-kuttl \
RUN echo "${USER_NAME}:x:${USER_UID}:0:${USER_NAME} user:${HOME}:/sbin/nologin" >> /etc/passwd

WORKDIR ${HOME}
# kuttl writes a kubeconfig file in the current working directory
RUN chmod g+w "${HOME}"

COPY --from=builder /workspace/build/scorecard-test-kuttl /usr/local/bin/scorecard-test-kuttl
COPY --from=builder /workspace/images/scorecard-test-kuttl/entrypoint /usr/local/bin/entrypoint
Expand Down
4 changes: 2 additions & 2 deletions images/scorecard-test-kuttl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import (
// scorecard v1alpha3.TestStatus json format.
//
// The kuttl output is expected to be produced by kubectl-kuttl
// at /tmp/kuttl-test.json.
// at /tmp/kuttl-report.json.
func main() {

jsonFile, err := os.Open("/tmp/kuttl-test.json")
jsonFile, err := os.Open("/tmp/kuttl-report.json")
if err != nil {
printErrorStatus(fmt.Errorf("could not open kuttl report %v", err))
return
Expand Down
12 changes: 4 additions & 8 deletions internal/ansible/handler/logging_enqueue_annotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ var _ = Describe("LoggingEnqueueRequestForAnnotation", func() {
}
repl.SetGroupVersionKind(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"})

err := handler.SetOwnerAnnotations(podOwner, repl)
Expect(err).To(BeNil())
Expect(handler.SetOwnerAnnotations(podOwner, repl)).To(Succeed())

evt := event.CreateEvent{
Object: repl,
Expand Down Expand Up @@ -280,8 +279,7 @@ var _ = Describe("LoggingEnqueueRequestForAnnotation", func() {
newPod.Name = pod.Name + "2"
newPod.Namespace = pod.Namespace + "2"

err := handler.SetOwnerAnnotations(podOwner, pod)
Expect(err).To(BeNil())
Expect(handler.SetOwnerAnnotations(podOwner, pod)).To(Succeed())

evt := event.UpdateEvent{
ObjectOld: pod,
Expand Down Expand Up @@ -395,8 +393,7 @@ var _ = Describe("LoggingEnqueueRequestForAnnotation", func() {
newPod.Name = pod.Name + "2"
newPod.Namespace = pod.Namespace + "2"

err := handler.SetOwnerAnnotations(podOwner, pod)
Expect(err).To(BeNil())
Expect(handler.SetOwnerAnnotations(podOwner, pod)).To(Succeed())

var podOwner2 = &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -406,8 +403,7 @@ var _ = Describe("LoggingEnqueueRequestForAnnotation", func() {
}
podOwner2.SetGroupVersionKind(schema.GroupVersionKind{Group: "", Kind: "Pod"})

err = handler.SetOwnerAnnotations(podOwner2, newPod)
Expect(err).To(BeNil())
Expect(handler.SetOwnerAnnotations(podOwner2, newPod)).To(Succeed())

evt := event.UpdateEvent{
ObjectOld: pod,
Expand Down
41 changes: 18 additions & 23 deletions internal/ansible/handler/logging_enqueue_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ var _ = Describe("LoggingEnqueueRequestForObject", func() {
// verify metrics
gauges, err := metrics.Registry.Gather()
Expect(err).NotTo(HaveOccurred())
Expect(len(gauges)).To(Equal(1))
Expect(gauges).To(HaveLen(1))
assertMetrics(gauges[0], 1, []*corev1.Pod{pod})
})
})
Expand Down Expand Up @@ -119,7 +119,7 @@ var _ = Describe("LoggingEnqueueRequestForObject", func() {
// verify metrics
gauges, err := metrics.Registry.Gather()
Expect(err).NotTo(HaveOccurred())
Expect(len(gauges)).To(Equal(0))
Expect(gauges).To(BeEmpty())
})
})
Context("when a gauge does not exist", func() {
Expand Down Expand Up @@ -148,7 +148,7 @@ var _ = Describe("LoggingEnqueueRequestForObject", func() {
// verify metrics
gauges, err := metrics.Registry.Gather()
Expect(err).NotTo(HaveOccurred())
Expect(len(gauges)).To(Equal(0))
Expect(gauges).To(BeEmpty())
})
})

Expand Down Expand Up @@ -187,36 +187,31 @@ var _ = Describe("LoggingEnqueueRequestForObject", func() {
// verify metrics
gauges, err := metrics.Registry.Gather()
Expect(err).NotTo(HaveOccurred())
Expect(len(gauges)).To(Equal(1))
Expect(gauges).To(HaveLen(1))
assertMetrics(gauges[0], 2, []*corev1.Pod{newpod, pod})
})
})
})

func assertMetrics(gauge *dto.MetricFamily, count int, pods []*corev1.Pod) {
// need variables to compare the pointers
name := "name"
namespace := "namespace"
g := "group"
v := "version"
k := "kind"

Expect(len(gauge.Metric)).To(Equal(count))
Expect(gauge.Metric).To(HaveLen(count))
for i := 0; i < count; i++ {
Expect(*gauge.Metric[i].Gauge.Value).To(Equal(float64(pods[i].GetObjectMeta().GetCreationTimestamp().UTC().Unix())))

for _, l := range gauge.Metric[i].Label {
switch l.Name {
case &name:
Expect(l.Value).To(Equal(pods[i].GetObjectMeta().GetName()))
case &namespace:
Expect(l.Value).To(Equal(pods[i].GetObjectMeta().GetNamespace()))
case &g:
Expect(l.Value).To(Equal(pods[i].GetObjectKind().GroupVersionKind().Group))
case &v:
Expect(l.Value).To(Equal(pods[i].GetObjectKind().GroupVersionKind().Version))
case &k:
Expect(l.Value).To(Equal(pods[i].GetObjectKind().GroupVersionKind().Kind))
if l.Name != nil {
switch *l.Name {
case "name":
Expect(l.Value).To(HaveValue(Equal(pods[i].GetObjectMeta().GetName())))
case "namespace":
Expect(l.Value).To(HaveValue(Equal(pods[i].GetObjectMeta().GetNamespace())))
case "group":
Expect(l.Value).To(HaveValue(Equal(pods[i].GetObjectKind().GroupVersionKind().Group)))
case "version":
Expect(l.Value).To(HaveValue(Equal(pods[i].GetObjectKind().GroupVersionKind().Version)))
case "kind":
Expect(l.Value).To(HaveValue(Equal(pods[i].GetObjectKind().GroupVersionKind().Kind)))
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ansible/proxy/inject_owner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ var _ = Describe("injectOwnerReferenceHandler", func() {
}
ownerRefs := modifiedCM.ObjectMeta.OwnerReferences

Expect(len(ownerRefs)).To(Equal(1))
Expect(ownerRefs).To(HaveLen(1))

ownerRef := ownerRefs[0]

Expand Down
5 changes: 4 additions & 1 deletion internal/ansible/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ func (r *runner) makeParameters(u *unstructured.Unstructured) map[string]interfa

specKey := fmt.Sprintf("%s_spec", objKey)
parameters[specKey] = spec
if r.markUnsafe {
parameters[specKey] = markUnsafe(spec)
}

for k, v := range r.Vars {
parameters[k] = v
Expand All @@ -391,7 +394,7 @@ func (r *runner) makeParameters(u *unstructured.Unstructured) map[string]interfa
func markUnsafe(values interface{}) interface{} {
switch v := values.(type) {
case []interface{}:
var p []interface{}
p := make([]interface{}, 0)
for _, n := range v {
p = append(p, markUnsafe(n))
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/ansible-operator/version/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var _ = Describe("Running a version command", func() {
w.Close()
}()
stdout, err := io.ReadAll(r)
Expect(err).To(BeNil())
Expect(err).ToNot(HaveOccurred())
stdoutString := string(stdout)
version := ver.GitVersion
if version == "unknown" {
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/helm-operator/version/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var _ = Describe("Running a version command", func() {
w.Close()
}()
stdout, err := io.ReadAll(r)
Expect(err).To(BeNil())
Expect(err).ToNot(HaveOccurred())
stdoutString := string(stdout)
version := ver.GitVersion
if version == "unknown" {
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/operator-sdk/bundle/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var _ = Describe("Running a bundle command", func() {
Expect(cmd).NotTo(BeNil())

subcommands := cmd.Commands()
Expect(len(subcommands)).To(Equal(1))
Expect(subcommands).To(HaveLen(1))
Expect(subcommands[0].Use).To(Equal("validate"))
})
})
Expand Down
Loading