Skip to content

Commit 99a96b2

Browse files
committed
commands/.../test/local,pkg/test: add image flag to test local
1 parent be39b2d commit 99a96b2

File tree

4 files changed

+93
-7
lines changed

4 files changed

+93
-7
lines changed

Diff for: Gopkg.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: commands/operator-sdk/cmd/test/local.go

+87-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package cmdtest
1616

1717
import (
18+
"bytes"
1819
"fmt"
1920
"io/ioutil"
2021
"os"
@@ -27,8 +28,13 @@ import (
2728
"github.com/operator-framework/operator-sdk/pkg/scaffold"
2829
"github.com/operator-framework/operator-sdk/pkg/test"
2930

31+
"github.com/ghodss/yaml"
3032
log "github.com/sirupsen/logrus"
3133
"github.com/spf13/cobra"
34+
appsv1 "k8s.io/api/apps/v1"
35+
"k8s.io/apimachinery/pkg/runtime"
36+
"k8s.io/apimachinery/pkg/runtime/serializer"
37+
cgoscheme "k8s.io/client-go/kubernetes/scheme"
3238
)
3339

3440
var deployTestDir = filepath.Join(scaffold.DeployDir, "test")
@@ -39,6 +45,7 @@ type testLocalConfig struct {
3945
namespacedManPath string
4046
goTestFlags string
4147
namespace string
48+
image string
4249
}
4350

4451
var tlConfig testLocalConfig
@@ -54,6 +61,7 @@ func NewTestLocalCmd() *cobra.Command {
5461
testCmd.Flags().StringVar(&tlConfig.namespacedManPath, "namespaced-manifest", "", "Path to manifest for per-test, namespaced resources (e.g. RBAC and Operator manifest)")
5562
testCmd.Flags().StringVar(&tlConfig.goTestFlags, "go-test-flags", "", "Additional flags to pass to go test")
5663
testCmd.Flags().StringVar(&tlConfig.namespace, "namespace", "", "If non-empty, single namespace to run tests in")
64+
testCmd.Flags().StringVar(&tlConfig.image, "image", "", "Use a different image from the one specified in the namespaced manifest")
5765

5866
return testCmd
5967
}
@@ -98,12 +106,14 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
98106
if err != nil {
99107
log.Fatalf("could not create temporary namespaced manifest file: (%v)", err)
100108
}
101-
defer func() {
102-
err := os.Remove(tlConfig.namespacedManPath)
103-
if err != nil {
104-
log.Fatalf("could not delete temporary namespace manifest file: (%v)", err)
105-
}
106-
}()
109+
/*
110+
defer func() {
111+
err := os.Remove(tlConfig.namespacedManPath)
112+
if err != nil {
113+
log.Fatalf("could not delete temporary namespace manifest file: (%v)", err)
114+
}
115+
}()
116+
*/
107117
}
108118
if tlConfig.globalManPath == "" {
109119
err := os.MkdirAll(deployTestDir, os.FileMode(fileutil.DefaultDirFileMode))
@@ -141,6 +151,11 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
141151
}
142152
}()
143153
}
154+
if tlConfig.image != "" {
155+
if err := replaceImage(tlConfig.namespacedManPath, tlConfig.image); err != nil {
156+
log.Fatalf("replaceImage function failed: %v", err)
157+
}
158+
}
144159
testArgs := []string{"test", args[0] + "/..."}
145160
testArgs = append(testArgs, "-"+test.KubeConfigFlag, tlConfig.kubeconfig)
146161
testArgs = append(testArgs, "-"+test.NamespacedManPathFlag, tlConfig.namespacedManPath)
@@ -176,3 +191,69 @@ func combineManifests(base, manifest []byte) []byte {
176191
}
177192
return base
178193
}
194+
195+
// TODO: add support for multiple deployments and containers (user would have to
196+
// provide extra information in that case)
197+
198+
// replaceImage searches for a deployment and replaces the image in the container
199+
// to the one specified in the function call. The function will fail if the
200+
// number of deployments is not equal to one or if the deployment has multiple
201+
// containers
202+
func replaceImage(manifestPath, image string) error {
203+
yamlFile, err := ioutil.ReadFile(manifestPath)
204+
if err != nil {
205+
return err
206+
}
207+
foundDeployment := false
208+
newManifest := []byte{}
209+
yamlSplit := bytes.Split(yamlFile, []byte("\n---\n"))
210+
for _, yamlSpec := range yamlSplit {
211+
if string(yamlSpec) == "" {
212+
continue
213+
}
214+
decoded := make(map[string]interface{})
215+
err = yaml.Unmarshal(yamlSpec, &decoded)
216+
if err != nil {
217+
return err
218+
}
219+
kind, ok := decoded["kind"].(string)
220+
if !ok {
221+
newManifest = combineManifests(newManifest, yamlSpec)
222+
continue
223+
}
224+
if kind != "Deployment" {
225+
newManifest = combineManifests(newManifest, yamlSpec)
226+
continue
227+
}
228+
if foundDeployment {
229+
return fmt.Errorf("cannot use `image` flag on namespaced manifest with more than 1 deployment")
230+
}
231+
foundDeployment = true
232+
scheme := runtime.NewScheme()
233+
// scheme for client go
234+
cgoscheme.AddToScheme(scheme)
235+
dynamicDecoder := serializer.NewCodecFactory(scheme).UniversalDeserializer()
236+
237+
obj, _, err := dynamicDecoder.Decode(yamlSpec, nil, nil)
238+
if err != nil {
239+
return err
240+
}
241+
dep := &appsv1.Deployment{}
242+
switch o := obj.(type) {
243+
case *appsv1.Deployment:
244+
dep = o
245+
default:
246+
return fmt.Errorf("error in replaceImage switch case; could not convert runtime.Object to deployment")
247+
}
248+
if len(dep.Spec.Template.Spec.Containers) != 1 {
249+
return fmt.Errorf("cannot use `image` flag on namespaced manifest containing more than 1 container in the operator deployment")
250+
}
251+
dep.Spec.Template.Spec.Containers[0].Image = image
252+
updatedYamlSpec, err := yaml.Marshal(dep)
253+
if err != nil {
254+
return fmt.Errorf("failed to convert deployment object back to yaml: %v", err)
255+
}
256+
newManifest = combineManifests(newManifest, updatedYamlSpec)
257+
}
258+
return ioutil.WriteFile(manifestPath, newManifest, fileutil.DefaultFileMode)
259+
}

Diff for: hack/tests/test-subcommand.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cd test/test-framework
55
# test framework with defaults
66
operator-sdk test local .
77
# test operator-sdk test flags
8-
operator-sdk test local . --global-manifest deploy/crds/cache_v1alpha1_memcached_crd.yaml --namespaced-manifest deploy/namespace-init.yaml --go-test-flags "-parallel 1" --kubeconfig $HOME/.kube/config
8+
operator-sdk test local . --global-manifest deploy/crds/cache_v1alpha1_memcached_crd.yaml --namespaced-manifest deploy/namespace-init.yaml --go-test-flags "-parallel 1" --kubeconfig $HOME/.kube/config --image=quay.io/coreos/operator-sdk-dev:test-framework-operator-runtime
99
# test operator-sdk test local single namespace mode
1010
kubectl create namespace test-memcached
1111
operator-sdk test local . --namespace=test-memcached

Diff for: pkg/test/resource_creator.go

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ func (ctx *TestCtx) createFromYAML(yamlFile []byte, skipIfExists bool, cleanupOp
6666
}
6767
yamlSplit := bytes.Split(yamlFile, []byte("\n---\n"))
6868
for _, yamlSpec := range yamlSplit {
69+
// some autogenerated files may include an extra `---` at the end of the file
70+
if string(yamlSpec) == "" {
71+
continue
72+
}
6973
yamlSpec, err = setNamespaceYAML(yamlSpec, namespace)
7074
if err != nil {
7175
return err

0 commit comments

Comments
 (0)