/
api.go
171 lines (142 loc) · 3.85 KB
/
api.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
package cmd
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"strings"
"github.com/bakito/helm-patch/pkg/types"
"github.com/bakito/helm-patch/pkg/util"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/release"
"sigs.k8s.io/yaml"
)
func newAPICmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "api [flags] RELEASE",
Short: "path the api version of a resource",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("name of release to be patched has to be defined")
}
return nil
},
RunE: runAPI,
}
flags := cmd.Flags()
settings.AddFlags(flags)
flags.StringVarP(&kind, "kind", "k", "", "the kind to patch the api version")
flags.StringVar(&from, "from", "", "the api version that has to be replaced")
flags.StringVar(&to, "to", "", "the api version to be set")
flags.StringVarP(&name, "name", "n", "", "the name of the resource")
flags.IntVar(&revision, "revision", -1, "the revision of the release to path")
cmd.MarkFlagRequired("kind")
cmd.MarkFlagRequired("to")
return cmd
}
func runAPI(cmd *cobra.Command, args []string) error {
apiOptions := apiOptions{
Options: types.Options{
DryRun: settings.dryRun,
ReleaseName: args[0],
Revision: revision,
},
kind: kind,
from: from,
to: to,
resourceName: name,
}
return patchAPI(apiOptions)
}
func patchAPI(opts apiOptions) error {
dr := ""
if opts.DryRun {
log.Println("NOTE: This is in dry-run mode, the following actions will not be executed.")
log.Println("Run without --dry-run to take the actions described below:")
log.Println()
dr = "DRY-RUN "
}
cfg, err := settings.cfg()
if err != nil {
return err
}
releases, err := cfg.Releases.List(opts.Filter())
if err != nil {
return err
}
var rel *release.Release
if len(releases) > 0 {
rel = releases[len(releases)-1]
}
log.Printf("%sProcessing release: '%s' with revision: %v\n", dr, rel.Name, rel.Version)
changed := false
manifests := util.SplitManifests(rel.Manifest)
for name, data := range manifests {
resource := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(data), &resource); err != nil {
return err
}
if i := apiInfo(opts, resource); i != nil {
p, err := patchManifest(opts, resource, i)
if err != nil {
return err
} else if p != "" {
manifests[name] = p
changed = true
}
}
}
if changed {
if !opts.DryRun {
err = saveResource(manifests, rel, cfg)
if err != nil {
return err
}
}
log.Printf("%sRelease: '%s' with revision: %v patched successfully\n", dr, rel.Name, rel.Version)
} else {
log.Printf("%sNothing to patch\n", dr)
}
return nil
}
func saveResource(manifests map[string]string, rel *release.Release, cfg *action.Configuration) error {
b := bytes.NewBuffer(nil)
for name, content := range manifests {
if strings.TrimSpace(content) == "" {
continue
}
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
}
rel.Manifest = b.String()
return cfg.Releases.Update(rel)
}
func patchManifest(opts apiOptions, resource map[string]interface{}, r types.Resource) (string, error) {
log.Printf("Patching kind: %s name: %s from apiVersion: %s to apiVersion: %s\n", r.Kind(), r.Name(), opts.from, opts.to)
resource["apiVersion"] = opts.to
m, err := yaml.Marshal(resource)
if err == nil {
return string(m), nil
}
return "", nil
}
func apiInfo(opts apiOptions, yaml map[string]interface{}) types.Resource {
resource := types.ToResource(yaml)
if resource == nil {
return nil
}
name := resource.Name()
if name == "" || (name != opts.resourceName && opts.resourceName != "") {
return nil
}
k := resource.Kind()
if k == "" || k != opts.kind {
return nil
}
version := resource.GroupVersion()
if version == "" || (version != opts.from && opts.from != "") {
return nil
}
return resource
}