-
Notifications
You must be signed in to change notification settings - Fork 49
/
ingress.go
185 lines (167 loc) · 5.77 KB
/
ingress.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
package ingress
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/jenkins-x-plugins/jx-gitops/pkg/rootcmd"
jxcore "github.com/jenkins-x/jx-api/v4/pkg/apis/core/v4beta1"
"github.com/jenkins-x/jx-helpers/v3/pkg/cobras/helper"
"github.com/jenkins-x/jx-helpers/v3/pkg/cobras/templates"
"github.com/jenkins-x/jx-helpers/v3/pkg/files"
"github.com/jenkins-x/jx-helpers/v3/pkg/termcolor"
"github.com/jenkins-x/jx-logging/v3/pkg/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
nv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/yaml"
)
var (
ingressLong = templates.LongDesc(`
Updates Ingress resources with the current ingress domain
`)
ingressExample = templates.Examples(`
# updates any newly created Ingress resources to the new domain
%s ingress
`)
)
// IngressOptions the options for the command
type Options struct {
Dir string
ReplaceDomain string
BatchMode bool
FailOnYAMLParseError bool
}
// NewCmdUpdate creates a command object for the command
func NewCmdUpdateIngress() (*cobra.Command, *Options) {
o := &Options{}
cmd := &cobra.Command{
Use: "ingress",
Short: "Updates Ingress resources with the current ingress domain",
Long: ingressLong,
Example: fmt.Sprintf(ingressExample, rootcmd.BinaryName),
Run: func(_ *cobra.Command, _ []string) {
err := o.Run()
helper.CheckErr(err)
},
}
cmd.Flags().StringVarP(&o.Dir, "dir", "d", ".", "the directory to look for a 'jx-apps.yml' file")
cmd.Flags().StringVarP(&o.ReplaceDomain, "domain", "n", "cluster.local", "the domain to replace with whats in jx-requirements.yml")
cmd.Flags().BoolVarP(&o.FailOnYAMLParseError, "fail-on-parse-error", "", false, "if enabled we fail if we cannot parse a yaml file as a kubernetes resource")
return cmd, o
}
// Run implements the command
func (o *Options) Run() error {
requirementsResource, requirementsFileName, err := jxcore.LoadRequirementsConfig(o.Dir, false)
if err != nil {
return errors.Wrapf(err, "failed to load requirements in dir %s", o.Dir)
}
requirements := &requirementsResource.Spec
newDomain := requirements.Ingress.Domain
if newDomain == "" {
log.Logger().Warnf("not modifying Ingress resources as the requirements file %s does not contain Ingress.Domain", requirementsFileName)
return nil
}
tlsEnabled := requirements.Ingress.TLS.Enabled
log.Logger().Infof("replacing ingress domain %s to %s with TLS: %v", termcolor.ColorInfo(o.ReplaceDomain), termcolor.ColorInfo(newDomain), tlsEnabled)
fn := func(ing *nv1.Ingress, path string) (bool, error) {
modified := false
s := &ing.Spec
for i, r := range s.Rules {
currentHost := r.Host
host := o.modifyHost(currentHost, newDomain)
if host != "" && host != currentHost {
modified = true
s.Rules[i].Host = host
log.Logger().Infof("ingress at %s updated to %s", termcolor.ColorInfo(path), termcolor.ColorInfo(host))
} else {
log.Logger().Infof("ingress at %s does not match domain as is %s", termcolor.ColorInfo(path), termcolor.ColorInfo(currentHost))
}
}
for i, tls := range s.TLS {
hosts := tls.Hosts
for j, currentHost := range hosts {
host := o.modifyHost(currentHost, newDomain)
if host != "" && host != currentHost {
modified = true
if !tlsEnabled {
log.Logger().Infof("ingress at %s disabling TLS", termcolor.ColorInfo(path))
s.TLS = nil
break
}
log.Logger().Infof("ingress at %s updated to %s", termcolor.ColorInfo(path), termcolor.ColorInfo(host))
hosts[j] = host
s.TLS[i].Hosts = hosts
} else {
log.Logger().Infof("ingress at %s does not match domain as is %s", termcolor.ColorInfo(path), termcolor.ColorInfo(currentHost))
}
}
}
return modified, nil
}
return o.updateIngresses(o.Dir, fn)
}
// modifyHost modifies the host name if it matches the predicate otherwise return an empty string
func (o *Options) modifyHost(host, newDomain string) string {
if strings.HasSuffix(host, o.ReplaceDomain) {
return strings.TrimSuffix(host, o.ReplaceDomain) + newDomain
}
return ""
}
func (o *Options) updateIngresses(dir string, fn func(ing *nv1.Ingress, path string) (bool, error)) error {
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { //nolint:staticcheck
if info == nil || info.IsDir() {
return nil
}
if !strings.HasSuffix(path, ".yaml") && !strings.HasSuffix(path, ".yml") {
return nil
}
// ignore some files
_, fileName := filepath.Split(path)
if fileName == "jx-requirements.yml" || fileName == "jenkins-x.yml" {
return nil
}
data, err := os.ReadFile(path) //nolint:staticcheck
if err != nil {
return errors.Wrapf(err, "failed to load file %s", path)
}
obj := &unstructured.Unstructured{}
err = yaml.Unmarshal(data, obj)
if err != nil {
if o.FailOnYAMLParseError {
return errors.Wrapf(err, "failed to unmarshal YAML in file %s", path)
}
log.Logger().Infof("could not parse YAML file %s", path)
return nil
}
if obj.GetKind() != "Ingress" {
return nil
}
apiVersion := obj.GetAPIVersion()
if apiVersion != "networking.k8s.io/v1" && apiVersion != "networking.k8s.io/v1beta1" && apiVersion != "extensions/v1beta1" {
return nil
}
ing := &nv1.Ingress{}
err = yaml.Unmarshal(data, ing)
if err != nil {
return errors.Wrapf(err, "failed to unmarshal YAML as Ingress in file %s", path)
}
modified, err := fn(ing, path)
if err != nil {
return errors.Wrapf(err, "failed to modify Ingress at %s", path)
}
if !modified {
return nil
}
data, err = yaml.Marshal(ing)
if err != nil {
return errors.Wrap(err, "failed to marshal ingress onject to yaml")
}
err = os.WriteFile(path, data, files.DefaultFileWritePermissions)
if err != nil {
return errors.Wrapf(err, "failed to save %s", path)
}
return nil
})
}