This repository has been archived by the owner on May 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
/
crdhandler.go
275 lines (241 loc) · 9.8 KB
/
crdhandler.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
268
269
270
271
272
273
274
275
/*
Copyright (C) 2019 Synopsys, Inc.
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 blackduck
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
blackduckv1 "github.com/blackducksoftware/synopsys-operator/pkg/api/blackduck/v1"
"github.com/blackducksoftware/synopsys-operator/pkg/apps"
blackduckclientset "github.com/blackducksoftware/synopsys-operator/pkg/blackduck/client/clientset/versioned"
blackduckutils "github.com/blackducksoftware/synopsys-operator/pkg/blackduck/util"
"github.com/blackducksoftware/synopsys-operator/pkg/protoform"
"github.com/blackducksoftware/synopsys-operator/pkg/util"
"github.com/imdario/mergo"
routeclient "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1"
securityclient "github.com/openshift/client-go/security/clientset/versioned/typed/security/v1"
log "github.com/sirupsen/logrus"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
// HandlerInterface interface contains the methods that are required
type HandlerInterface interface {
ObjectCreated(obj interface{})
ObjectDeleted(obj string)
ObjectUpdated(objOld, objNew interface{})
}
// State contains the state of the Black Duck
type State string
// DesiredState contains the desired state of the Black Duck
type DesiredState string
const (
// Running is used when the instance is running
Running State = "Running"
// Starting is used when the instance is starting
Starting State = "Starting"
// Stopped is used when the instance is about to stop
Stopped State = "Stopped"
// Error is used when the instance deployment errored out
Error State = "Error"
// DbMigration is used when the instance is about to be in the migrated state
DbMigration DesiredState = "DbMigration"
// Start is used when the instance to be created or updated
Start DesiredState = ""
// Stop is used when the instance to be stopped
Stop DesiredState = "Stop"
// DbMigrate is used when the instance is migrated
DbMigrate DesiredState = "DbMigrate"
)
// Handler will store the configuration that is required to initiantiate the informers callback
type Handler struct {
config *protoform.Config
kubeConfig *rest.Config
kubeClient *kubernetes.Clientset
blackduckClient *blackduckclientset.Clientset
defaults *blackduckv1.BlackduckSpec
cmMutex chan bool
osSecurityClient *securityclient.SecurityV1Client
routeClient *routeclient.RouteV1Client
}
// NewHandler will create the handler
func NewHandler(config *protoform.Config, kubeConfig *rest.Config, kubeClient *kubernetes.Clientset, hubClient *blackduckclientset.Clientset,
defaults *blackduckv1.BlackduckSpec, cmMutex chan bool, osSecurityClient *securityclient.SecurityV1Client, routeClient *routeclient.RouteV1Client) *Handler {
return &Handler{config: config, kubeConfig: kubeConfig, kubeClient: kubeClient, blackduckClient: hubClient, defaults: defaults,
cmMutex: cmMutex, osSecurityClient: osSecurityClient, routeClient: routeClient}
}
// APISetHubsRequest to set the Black Duck urls for Perceptor
type APISetHubsRequest struct {
HubURLs []string
}
// ObjectCreated will be called for create hub events
func (h *Handler) ObjectCreated(obj interface{}) {
h.ObjectUpdated(nil, obj)
}
// ObjectDeleted will be called for delete hub events
func (h *Handler) ObjectDeleted(name string) {
log.Debugf("in ObjectDeleted: %+v", name)
// if cluster scope, then check whether the Black Duck CRD exist. If not exist, then don't delete the instance
if h.config.IsClusterScoped {
apiClientset, err := clientset.NewForConfig(h.kubeConfig)
crd, err := apiClientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(util.BlackDuckCRDName, metav1.GetOptions{})
if err != nil || crd.DeletionTimestamp != nil {
// We do not delete the Black Duck instance if the CRD doesn't exist or that it is in the process of being deleted
log.Warnf("Ignoring request to delete %s because the %s CRD doesn't exist or is being deleted", name, util.BlackDuckCRDName)
return
}
}
// Voluntary deletion. The CRD still exists but the Black Duck resource has been deleted
app := apps.NewApp(h.config, h.kubeConfig)
err := app.Blackduck().Delete(name)
if err != nil {
log.Error(err)
}
}
// ObjectUpdated will be called for update black duck events
func (h *Handler) ObjectUpdated(objOld, objNew interface{}) {
var err error
bd, ok := objNew.(*blackduckv1.Blackduck)
if !ok {
log.Error("unable to cast Black Duck object")
return
}
if _, ok = bd.Annotations["synopsys.com/created.by"]; !ok {
bd.Annotations = util.InitAnnotations(bd.Annotations)
bd.Annotations["synopsys.com/created.by"] = h.config.Version
bd, err = util.UpdateBlackduck(h.blackduckClient, bd)
if err != nil {
log.Errorf("couldn't update the annotation for %s Black Duck instance in %s namespace due to %+v", bd.Name, bd.Spec.Namespace, err)
return
}
}
newSpec := bd.Spec
blackDuckDefaultSpec := h.defaults
err = mergo.Merge(&newSpec, blackDuckDefaultSpec)
if err != nil {
log.Errorf("unable to merge the Black Duck structs for %s due to %+v", bd.Name, err)
bd, err = blackduckutils.UpdateState(h.blackduckClient, bd.Name, h.config.CrdNamespace, string(Error), err)
if err != nil {
log.Errorf("couldn't update the Black Duck state: %v", err)
}
return
}
bd.Spec = newSpec
// An error occurred. We wait for one minute before we try to ensure again
if strings.EqualFold(bd.Status.State, string(Error)) {
time.Sleep(time.Minute * 1)
}
log.Debugf("ObjectUpdated: %s", bd.Name)
// Ensure the replication controller pods are zero for upgrade scenario
rcs, _ := util.ListReplicationControllers(h.kubeClient, bd.Spec.Namespace, "app=blackduck,component!=postgres")
for _, rc := range rcs.Items {
if rc.Spec.Replicas == util.IntToInt32(0) {
if err := util.EnsureFilterPodsByNamePrefixInNamespaceToZero(h.kubeClient, bd.Spec.Namespace, rc.Name); err != nil {
log.Error(err)
return
}
}
}
// Ensure
app := apps.NewApp(h.config, h.kubeConfig)
err = app.Blackduck().Ensure(bd)
if err != nil {
log.Error(err)
bd, err = blackduckutils.UpdateState(h.blackduckClient, bd.Name, h.config.CrdNamespace, string(Error), err)
if err != nil {
log.Errorf("couldn't update the state for %s Black Duck instance in %s namespace due to %+v", bd.Name, bd.Spec.Namespace, err)
}
return
}
if strings.EqualFold(bd.Spec.DesiredState, string(Stop)) { // Stop State
if !strings.EqualFold(bd.Status.State, string(Stopped)) {
bd, err = blackduckutils.UpdateState(h.blackduckClient, bd.Name, h.config.CrdNamespace, string(Stopped), nil)
if err != nil {
log.Errorf("couldn't update the state for %s Black Duck instance in %s namespace due to %+v", bd.Name, bd.Spec.Namespace, err)
}
}
} else if strings.EqualFold(bd.Spec.DesiredState, string(DbMigrate)) { // DbMigrate State
if !strings.EqualFold(bd.Status.State, string(DbMigration)) {
bd, err = blackduckutils.UpdateState(h.blackduckClient, bd.Name, h.config.CrdNamespace, string(DbMigration), nil)
if err != nil {
log.Errorf("couldn't update the state for %s Black Duck instance in %s namespace due to %+v", bd.Name, bd.Spec.Namespace, err)
}
}
} else { // Start, Running, and Error States
if !strings.EqualFold(bd.Status.State, string(Running)) {
// Verify that we can access the Black Duck
blackDuckURL := fmt.Sprintf("%s.%s.svc", util.GetResourceName(bd.Name, util.BlackDuckName, "webserver"), bd.Spec.Namespace)
status := h.verifyHub(blackDuckURL, bd.Spec.Namespace, bd.Name)
if status { // Set state to Running if we can access the Black Duck
bd, err = blackduckutils.UpdateState(h.blackduckClient, bd.Name, h.config.CrdNamespace, string(Running), nil)
if err != nil {
log.Errorf("couldn't update the state for %s Black Duck instance in %s namespace due to %+v", bd.Name, bd.Spec.Namespace, err)
}
}
}
}
}
func (h *Handler) verifyHub(blackDuckURL string, namespace string, name string) bool {
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
Timeout: 5 * time.Second,
}
for i := 0; i < 10; i++ {
resp, err := client.Get(fmt.Sprintf("https://%s:443/api/current-version", blackDuckURL))
if err != nil {
log.Debugf("unable to talk with the Black Duck %s", blackDuckURL)
time.Sleep(10 * time.Second)
_, err := util.GetBlackduck(h.blackduckClient, h.config.CrdNamespace, name, metav1.GetOptions{})
if err != nil {
return false
}
continue
}
_, err = ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
log.Debugf("response status for %s Black Duck instance in %s namespace is %v", name, namespace, resp.Status)
if resp.StatusCode == 200 {
return true
}
time.Sleep(10 * time.Second)
}
return false
}
func (h *Handler) isBinaryAnalysisEnabled(envs []string) bool {
for _, value := range envs {
if strings.Contains(value, "USE_BINARY_UPLOADS") {
values := strings.SplitN(value, ":", 2)
if len(values) == 2 {
mapValue := strings.TrimSpace(values[1])
if strings.EqualFold(mapValue, "1") {
return true
}
}
return false
}
}
return false
}