/
init.go
267 lines (229 loc) · 7.92 KB
/
init.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// Copyright 2020 The Operator-SDK 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 ansible
import (
"fmt"
"os"
"path/filepath"
"github.com/spf13/pflag"
"sigs.k8s.io/kubebuilder/v3/pkg/config"
"sigs.k8s.io/kubebuilder/v3/pkg/machinery"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin"
"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
"github.com/operator-framework/ansible-operator-plugins/pkg/plugins/ansible/v1/scaffolds"
sdkpluginutil "github.com/operator-framework/ansible-operator-plugins/pkg/plugins/util"
)
const (
groupFlag = "group"
versionFlag = "version"
kindFlag = "kind"
)
var _ plugin.InitSubcommand = &initSubcommand{}
type initSubcommand struct {
// Wrapped plugin that we will call at post-scaffold
apiSubcommand createAPISubcommand
config config.Config
// For help text.
commandName string
// Flags
group string
version string
kind string
}
// UpdateContext injects documentation for the command
func (p *initSubcommand) UpdateMetadata(cliMeta plugin.CLIMetadata, subcmdMeta *plugin.SubcommandMetadata) {
subcmdMeta.Description = `
Initialize a new Ansible-based operator project.
Writes the following files
- a kubebuilder PROJECT file with the domain and project layout configuration
- a Makefile that provides an interface for building and managing the operator
- Kubernetes manifests and kustomize configuration
- a watches.yaml file that defines the mapping between APIs and Roles/Playbooks
Optionally creates a new API, using the same flags as "create api"
`
subcmdMeta.Examples = fmt.Sprintf(`
# Scaffold a project with no API
$ %[1]s init --plugins=%[2]s --domain=my.domain \
# Invokes "create api"
$ %[1]s init --plugins=%[2]s \
--domain=my.domain \
--group=apps --version=v1alpha1 --kind=AppService
$ %[1]s init --plugins=%[2]s \
--domain=my.domain \
--group=apps --version=v1alpha1 --kind=AppService \
--generate-role
$ %[1]s init --plugins=%[2]s \
--domain=my.domain \
--group=apps --version=v1alpha1 --kind=AppService \
--generate-playbook
$ %[1]s init --plugins=%[2]s \
--domain=my.domain \
--group=apps --version=v1alpha1 --kind=AppService \
--generate-playbook \
--generate-role
`, cliMeta.CommandName, pluginKey)
p.commandName = cliMeta.CommandName
}
func (p *initSubcommand) BindFlags(fs *pflag.FlagSet) {
fs.SortFlags = false
fs.StringVar(&p.group, "group", "", "resource Group")
fs.StringVar(&p.version, "version", "", "resource Version")
fs.StringVar(&p.kind, "kind", "", "resource Kind")
p.apiSubcommand.BindFlags(fs)
}
func (p *initSubcommand) InjectConfig(c config.Config) error {
p.config = c
return nil
}
func (p *initSubcommand) Scaffold(fs machinery.Filesystem) error {
if err := addInitCustomizations(p.config.GetProjectName(), p.config.IsComponentConfig()); err != nil {
return fmt.Errorf("error updating init manifests: %s", err)
}
scaffolder := scaffolds.NewInitScaffolder(p.config)
scaffolder.InjectFS(fs)
return scaffolder.Scaffold()
}
func (p *initSubcommand) PostScaffold() error {
doAPI := p.group != "" || p.version != "" || p.kind != ""
if !doAPI {
fmt.Printf("Next: define a resource with:\n$ %s create api\n", p.commandName)
} else {
args := []string{"create", "api"}
// The following three checks should match the default values in sig.k8s.io/kubebuilder/v3/pkg/cli/resource.go
if p.group != "" {
args = append(args, fmt.Sprintf("--%s", groupFlag), p.group)
}
if p.version != "" {
args = append(args, fmt.Sprintf("--%s", versionFlag), p.version)
}
if p.kind != "" {
args = append(args, fmt.Sprintf("--%s", kindFlag), p.kind)
}
if p.apiSubcommand.options.CRDVersion != defaultCrdVersion {
args = append(args, fmt.Sprintf("--%s", crdVersionFlag), p.apiSubcommand.options.CRDVersion)
}
if p.apiSubcommand.options.DoPlaybook {
args = append(args, fmt.Sprintf("--%s", generatePlaybookFlag))
}
if p.apiSubcommand.options.DoRole {
args = append(args, fmt.Sprintf("--%s", generateRoleFlag))
}
if err := util.RunCmd("Creating the API", os.Args[0], args...); err != nil {
return err
}
}
return nil
}
// addInitCustomizations will perform the required customizations for this plugin on the common base
func addInitCustomizations(projectName string, componentConfig bool) error {
managerFile := filepath.Join("config", "manager", "manager.yaml")
managerProxyPatchFile := filepath.Join("config", "default", "manager_auth_proxy_patch.yaml")
// todo: we ought to use afero instead. Replace this methods to insert/update
// by https://github.com/kubernetes-sigs/kubebuilder/pull/2119
// Add leader election
if componentConfig {
err := util.InsertCode(managerFile,
"- /manager",
fmt.Sprintf("\n args:\n - --leader-election-id=%s", projectName))
if err != nil {
return err
}
err = util.InsertCode(managerProxyPatchFile,
"memory: 64Mi",
fmt.Sprintf("\n - name: manager\n args:\n - \"--leader-election-id=%s\"", projectName))
if err != nil {
return err
}
} else {
err := util.InsertCode(managerFile,
"--leader-elect",
fmt.Sprintf("\n - --leader-election-id=%s", projectName))
if err != nil {
return err
}
err = util.InsertCode(managerProxyPatchFile,
"- \"--leader-elect\"",
fmt.Sprintf("\n - \"--leader-election-id=%s\"", projectName))
if err != nil {
return err
}
}
// update default resource request and limits with bigger values
const resourcesLimitsFragment = ` resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
`
const resourcesLimitsAnsibleFragment = ` resources:
limits:
cpu: 500m
memory: 768Mi
requests:
cpu: 10m
memory: 256Mi
`
err := util.ReplaceInFile(managerFile, resourcesLimitsFragment, resourcesLimitsAnsibleFragment)
if err != nil {
return err
}
// Add ANSIBLE_GATHERING env var
const envVar = `
env:
- name: ANSIBLE_GATHERING
value: explicit`
err = util.InsertCode(managerFile, "name: manager", envVar)
if err != nil {
return err
}
// replace the default ports because ansible has been using another one
// todo: remove it when we be able to change the port for the default one
// issue: https://github.com/operator-framework/ansible-operator-plugins/issues/4331
err = util.ReplaceInFile(managerFile, "port: 8081", "port: 6789")
if err != nil {
return err
}
if componentConfig {
managerConfigFile := filepath.Join("config", "manager", "controller_manager_config.yaml")
err = util.ReplaceInFile(managerConfigFile, "8081", "6789")
if err != nil {
return err
}
// Remove the webhook option for the componentConfig since webhooks are not supported by ansible
err = util.ReplaceInFile(managerConfigFile, "webhook:\n port: 9443", "")
if err != nil {
return err
}
} else {
err = util.ReplaceInFile(managerProxyPatchFile, "8081", "6789")
if err != nil {
return err
}
}
// Remove the call to the command as manager. Helm/Ansible has not been exposing this entrypoint
// todo: provide the manager entrypoint for helm/ansible and then remove it
const command = `command:
- /manager
`
err = util.ReplaceInFile(managerFile, command, "")
if err != nil {
return err
}
if err := sdkpluginutil.UpdateKustomizationsInit(); err != nil {
return fmt.Errorf("error updating kustomization.yaml files: %v", err)
}
return nil
}