Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit ff30a96

Browse files
ostromartistio-testing
authored andcommitted
Operator init and remove commands (#643)
* Operator init and remove commands * Build fix * Restore deleted files * Review comments * Fix merge conflicts * Regen assets * Lint
1 parent d4d1026 commit ff30a96

29 files changed

+2029
-236
lines changed

cmd/mesh/manifest-common.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"strings"
2222
"time"
2323

24+
"istio.io/operator/pkg/kubectlcmd"
25+
2426
"github.com/ghodss/yaml"
2527

2628
"istio.io/operator/pkg/apis/istio/v1alpha2"
@@ -53,7 +55,7 @@ func genApplyManifests(setOverlay []string, inFilename string, force bool, dryRu
5355
if err != nil {
5456
return fmt.Errorf("failed to generate manifest: %v", err)
5557
}
56-
opts := &manifest.InstallOptions{
58+
opts := &kubectlcmd.Options{
5759
DryRun: dryRun,
5860
Verbose: verbose,
5961
WaitTimeout: waitTimeout,

cmd/mesh/manifest-migrate.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,11 @@ func migrateFromClusterConfig(rootArgs *rootArgs, mmArgs *manifestMigrateArgs, l
119119
l.logAndPrint("translating in cluster specs\n")
120120

121121
c := kubectlcmd.New()
122-
output, stderr, err := c.GetConfig("", "", "istio-sidecar-injector",
123-
mmArgs.namespace, "jsonpath='{.data.values}'")
122+
opts := &kubectlcmd.Options{
123+
Namespace: mmArgs.namespace,
124+
ExtraArgs: []string{"jsonpath='{.data.values}'"},
125+
}
126+
output, stderr, err := c.GetConfigMap("istio-sidecar-injector", opts)
124127
if err != nil {
125128
return err
126129
}

cmd/mesh/operator-common.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2019 Istio Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mesh
16+
17+
import "istio.io/operator/pkg/manifest"
18+
19+
const (
20+
operatorResourceName = "istio-operator"
21+
)
22+
23+
func isControllerInstalled(kubeconfig, context, operatorNamespace string) (bool, error) {
24+
return manifest.DeploymentExists(kubeconfig, context, operatorNamespace, operatorResourceName)
25+
}

cmd/mesh/operator-init.go

+279
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Copyright 2019 Istio Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mesh
16+
17+
import (
18+
"fmt"
19+
"io/ioutil"
20+
"sort"
21+
"strings"
22+
"time"
23+
24+
"github.com/spf13/cobra"
25+
"k8s.io/utils/pointer"
26+
27+
"istio.io/operator/pkg/helm"
28+
"istio.io/operator/pkg/kubectlcmd"
29+
"istio.io/operator/pkg/manifest"
30+
"istio.io/operator/pkg/name"
31+
"istio.io/operator/pkg/object"
32+
"istio.io/operator/pkg/util"
33+
"istio.io/operator/version"
34+
"istio.io/pkg/log"
35+
buildversion "istio.io/pkg/version"
36+
)
37+
38+
type operatorInitArgs struct {
39+
// hub is the hub for the operator image.
40+
hub string
41+
// tag is the tag for the operator image.
42+
tag string
43+
// operatorNamespace is the namespace the operator controller is installed into.
44+
operatorNamespace string
45+
// istioNamespace is the namespace Istio is installed into.
46+
istioNamespace string
47+
// inFilename is the path to the input IstioControlPlane CR.
48+
inFilename string
49+
50+
// kubeConfigPath is the path to kube config file.
51+
kubeConfigPath string
52+
// context is the cluster context in the kube config.
53+
context string
54+
// readinessTimeout is maximum time to wait for all Istio resources to be ready.
55+
readinessTimeout time.Duration
56+
// wait is flag that indicates whether to wait resources ready before exiting.
57+
wait bool
58+
}
59+
60+
const (
61+
istioControllerComponentName = "Operator"
62+
istioNamespaceComponentName = "IstioNamespace"
63+
istioOperatorCRComponentName = "OperatorCustomResource"
64+
)
65+
66+
// manifestApplier is used for test dependency injection.
67+
type manifestApplier func(manifestStr, componentName string, opts *kubectlcmd.Options, verbose bool, l *Logger) bool
68+
69+
var (
70+
defaultManifestApplier = applyManifest
71+
)
72+
73+
func addOperatorInitFlags(cmd *cobra.Command, args *operatorInitArgs) {
74+
hub, tag := buildversion.DockerInfo.Hub, buildversion.DockerInfo.Tag
75+
if hub == "" {
76+
hub = "gcr.io/istio-testing"
77+
}
78+
if tag == "" {
79+
tag = "latest"
80+
}
81+
cmd.PersistentFlags().StringVarP(&args.inFilename, "filename", "f", "", filenameFlagHelpStr)
82+
cmd.PersistentFlags().StringVarP(&args.kubeConfigPath, "kubeconfig", "c", "", "Path to kube config")
83+
cmd.PersistentFlags().StringVar(&args.context, "context", "", "The name of the kubeconfig context to use")
84+
cmd.PersistentFlags().DurationVar(&args.readinessTimeout, "readiness-timeout", 300*time.Second, "Maximum seconds to wait for all Istio resources to be ready."+
85+
" The --wait flag must be set for this flag to apply")
86+
cmd.PersistentFlags().BoolVarP(&args.wait, "wait", "w", false, "Wait, if set will wait until all Pods, Services, and minimum number of Pods "+
87+
"of a Deployment are in a ready state before the command exits. It will wait for a maximum duration of --readiness-timeout seconds")
88+
89+
cmd.PersistentFlags().StringVar(&args.hub, "hub", hub, "The hub for the operator controller image")
90+
cmd.PersistentFlags().StringVar(&args.tag, "tag", tag, "The tag for the operator controller image")
91+
cmd.PersistentFlags().StringVar(&args.operatorNamespace, "operatorNamespace", "istio-operator",
92+
"The namespace the operator controller is installed into")
93+
cmd.PersistentFlags().StringVar(&args.istioNamespace, "istioNamespace", "istio-system",
94+
"The namespace Istio is installed into")
95+
}
96+
97+
func operatorInitCmd(rootArgs *rootArgs, oiArgs *operatorInitArgs) *cobra.Command {
98+
return &cobra.Command{
99+
Use: "init",
100+
Short: "Installs the Istio operator controller in the cluster.",
101+
Long: "The init subcommand installs the Istio operator controller in the cluster.",
102+
Args: cobra.ExactArgs(0),
103+
Run: func(cmd *cobra.Command, args []string) {
104+
l := NewLogger(rootArgs.logToStdErr, cmd.OutOrStdout(), cmd.OutOrStderr())
105+
operatorInit(rootArgs, oiArgs, l, defaultManifestApplier)
106+
}}
107+
}
108+
109+
// operatorInit installs the Istio operator controller into the cluster.
110+
func operatorInit(args *rootArgs, oiArgs *operatorInitArgs, l *Logger, apply manifestApplier) {
111+
initLogsOrExit(args)
112+
113+
// Error here likely indicates Deployment is missing. If some other K8s error, we will hit it again later.
114+
already, _ := isControllerInstalled(oiArgs.kubeConfigPath, oiArgs.context, oiArgs.operatorNamespace)
115+
if already {
116+
l.logAndPrintf("Operator controller is already installed in %s namespace, updating.", oiArgs.operatorNamespace)
117+
}
118+
119+
l.logAndPrintf("Using operator Deployment image: %s/operator:%s", oiArgs.hub, oiArgs.tag)
120+
121+
mstr, err := renderOperatorManifest(args, oiArgs, l)
122+
if err != nil {
123+
l.logAndFatal(err)
124+
}
125+
126+
log.Infof("Using the following manifest to install operator:\n%s\n", mstr)
127+
128+
// If CR was passed, we must create a namespace for it and install CR into it.
129+
customResource, istioNamespace, err := getCRAndNamespaceFromFile(oiArgs.inFilename, l)
130+
if err != nil {
131+
l.logAndFatal(err)
132+
}
133+
134+
opts := &kubectlcmd.Options{
135+
DryRun: args.dryRun,
136+
Verbose: args.verbose,
137+
WaitTimeout: 1 * time.Minute,
138+
Kubeconfig: oiArgs.kubeConfigPath,
139+
Context: oiArgs.context,
140+
}
141+
142+
if err := manifest.InitK8SRestClient(opts.Kubeconfig, opts.Context); err != nil {
143+
l.logAndFatal(err)
144+
}
145+
146+
success := apply(mstr, istioControllerComponentName, opts, args.verbose, l)
147+
148+
if customResource != "" {
149+
success = success && apply(genNamespaceResource(istioNamespace), istioNamespaceComponentName, opts, args.verbose, l)
150+
success = success && apply(customResource, istioOperatorCRComponentName, opts, args.verbose, l)
151+
}
152+
153+
if !success {
154+
l.logAndPrint("\n*** Errors were logged during apply operation. Please check component installation logs above. ***\n")
155+
return
156+
}
157+
158+
l.logAndPrint("\n*** Success. ***\n")
159+
}
160+
161+
func applyManifest(manifestStr, componentName string, opts *kubectlcmd.Options, verbose bool, l *Logger) bool {
162+
l.logAndPrint("")
163+
// Specifically don't prune operator installation since it leads to a lot of resources being reapplied.
164+
opts.Prune = pointer.BoolPtr(false)
165+
out, objs := manifest.ApplyManifest(name.ComponentName(componentName), manifestStr, version.OperatorBinaryVersion.String(), opts)
166+
167+
success := true
168+
if out.Err != nil {
169+
cs := fmt.Sprintf("Component %s install returned the following errors:", componentName)
170+
l.logAndPrintf("\n%s\n%s", cs, strings.Repeat("=", len(cs)))
171+
l.logAndPrint("Error: ", out.Err, "\n")
172+
success = false
173+
} else {
174+
l.logAndPrintf("Component %s installed successfully.", componentName)
175+
if opts.Verbose {
176+
l.logAndPrintf("The following objects were installed:\n%s", k8sObjectsString(objs))
177+
}
178+
}
179+
180+
if !ignoreError(out.Stderr) {
181+
l.logAndPrint("Error detail:\n", out.Stderr, "\n")
182+
success = false
183+
}
184+
if !ignoreError(out.Stderr) {
185+
l.logAndPrint(out.Stdout, "\n")
186+
}
187+
return success
188+
}
189+
190+
func getCRAndNamespaceFromFile(filePath string, l *Logger) (customResource string, istioNamespace string, err error) {
191+
if filePath == "" {
192+
return "", "", nil
193+
}
194+
195+
mergedYAML, err := genProfile(false, filePath, "", "", "", true, l)
196+
if err != nil {
197+
return "", "", err
198+
}
199+
mergedICPS, err := unmarshalAndValidateICPS(mergedYAML, true, l)
200+
if err != nil {
201+
return "", "", err
202+
}
203+
204+
b, err := ioutil.ReadFile(filePath)
205+
if err != nil {
206+
return "", "", fmt.Errorf("could not read values from file %s: %s", filePath, err)
207+
}
208+
customResource = string(b)
209+
istioNamespace = mergedICPS.DefaultNamespace
210+
return
211+
}
212+
213+
// chartsRootDir, helmBaseDir, componentName, namespace string) (TemplateRenderer, error) {
214+
func renderOperatorManifest(_ *rootArgs, oiArgs *operatorInitArgs, _ *Logger) (string, error) {
215+
r, err := helm.NewHelmRenderer("", "../operator", istioControllerComponentName, oiArgs.operatorNamespace)
216+
if err != nil {
217+
return "", err
218+
}
219+
220+
if err := r.Run(); err != nil {
221+
return "", err
222+
}
223+
224+
tmpl := `
225+
operatorNamespace: {{.OperatorNamespace}}
226+
istioNamespace: {{.IstioNamespace}}
227+
hub: {{.Hub}}
228+
tag: {{.Tag}}
229+
`
230+
231+
tv := struct {
232+
OperatorNamespace string
233+
IstioNamespace string
234+
Hub string
235+
Tag string
236+
}{
237+
OperatorNamespace: oiArgs.operatorNamespace,
238+
IstioNamespace: oiArgs.istioNamespace,
239+
Hub: oiArgs.hub,
240+
Tag: oiArgs.tag,
241+
}
242+
vals, err := util.RenderTemplate(tmpl, tv)
243+
if err != nil {
244+
return "", err
245+
}
246+
log.Infof("Installing operator charts with the following values:\n%s", vals)
247+
return r.RenderManifest(vals)
248+
}
249+
250+
func genNamespaceResource(namespace string) string {
251+
tmpl := `
252+
apiVersion: v1
253+
kind: Namespace
254+
metadata:
255+
labels:
256+
istio-injection: disabled
257+
name: {{.Namespace}}
258+
`
259+
260+
tv := struct {
261+
Namespace string
262+
}{
263+
Namespace: namespace,
264+
}
265+
vals, err := util.RenderTemplate(tmpl, tv)
266+
if err != nil {
267+
return ""
268+
}
269+
return vals
270+
}
271+
272+
func k8sObjectsString(objs object.K8sObjects) string {
273+
var out []string
274+
for _, o := range objs {
275+
out = append(out, fmt.Sprintf("- %s/%s/%s", o.Kind, o.Namespace, o.Name))
276+
}
277+
sort.Strings(out)
278+
return strings.Join(out, "\n")
279+
}

0 commit comments

Comments
 (0)