/
kubeHelper.go
160 lines (131 loc) · 5.29 KB
/
kubeHelper.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
package configOperator
import (
"github.com/aerogear/ups-config-operator/pkg/constants"
"fmt"
"os"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
"log"
"github.com/pkg/errors"
sc "github.com/kubernetes-incubator/service-catalog/pkg/client/clientset_generated/clientset"
"math/rand"
"k8s.io/apimachinery/pkg/watch"
)
type KubeHelper interface {
startSecretWatch() (watch.Interface, error)
listSecrets(selector string) (*v1.SecretList, error)
deleteSecret(name string)
getServiceBindingNameByID(bindingId string) (string, error)
findMobileClientConfig(clientId string) *v1.Secret
createClientConfigSecret(clientId string, serviceInstanceName string, serviceInstanceId string, pushAppId string) *v1.Secret
updateSecret(secret *v1.Secret) (*v1.Secret, error)
deleteServiceBinding(bindingName string) error
}
type KubeHelperImpl struct {
k8client *kubernetes.Clientset
scclient *sc.Clientset
}
func NewKubeHelper(k8client *kubernetes.Clientset, scclient *sc.Clientset) *KubeHelperImpl {
helper := new(KubeHelperImpl)
helper.k8client = k8client
helper.scclient = scclient
return helper
}
func (helper KubeHelperImpl) startSecretWatch() (watch.Interface, error) {
return helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).Watch(metav1.ListOptions{})
}
func (helper KubeHelperImpl) listSecrets(selector string) (*v1.SecretList, error) {
filter := metav1.ListOptions{LabelSelector: selector}
return helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).List(filter)
}
// Find a mobile client bound ups config secret
func (helper KubeHelperImpl) findMobileClientConfig(clientId string) *v1.Secret {
filter := metav1.ListOptions{LabelSelector: fmt.Sprintf("clientId=%s,serviceName=ups", clientId)}
secrets, err := helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).List(filter)
// TODO: remove error handling here!
if err != nil {
panic(err.Error())
}
// No secret exists yet, that's ok, we have to create one
if len(secrets.Items) == 0 {
return nil
}
// TODO: remove error handling here!
// Multiple secrets for the same clientId found, that's an error
if len(secrets.Items) > 1 {
panic(fmt.Sprintf("Multiple secrets found for clientId %s", clientId))
}
return &secrets.Items[0]
}
// Find a service binding by its ExternalID
func (helper KubeHelperImpl) getServiceBindingNameByID(bindingId string) (string, error) {
// Get a list of all service bindings in the namespace and find the one with a matching ExternalID
// This is not very efficient and could be improved with a jsonpath query but it looks like client-go
// does not support jsonpath or at least I could not find any examples.
bindings, err := helper.scclient.ServicecatalogV1beta1().ServiceBindings(os.Getenv(constants.EnvVarKeyNamespace)).List(metav1.ListOptions{})
if err != nil {
return "", err
}
for _, binding := range bindings.Items {
log.Printf("Checking service binding %s", binding.Name)
if binding.Spec.ExternalID == bindingId {
return binding.Name, nil
}
}
return "", errors.New(fmt.Sprintf("Can't find a binding with ExternalID %s", bindingId))
}
func (helper KubeHelperImpl) deleteServiceBinding(bindingName string) error {
return helper.scclient.ServicecatalogV1beta1().ServiceBindings(os.Getenv(constants.EnvVarKeyNamespace)).Delete(bindingName, nil)
}
// Creates a mobile client bound ups config secret
func (helper KubeHelperImpl) createClientConfigSecret(clientId string, serviceInstanceName string, serviceInstanceId string, pushAppId string) *v1.Secret {
configSecretName := fmt.Sprintf("ups-secret-%s-%s", clientId, getRandomIdentifier(5))
payload := v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: configSecretName,
Labels: map[string]string{
"mobile": "enabled",
"serviceName": "ups",
// Used by the mobile-cli to discover config objects
"serviceInstanceId": serviceInstanceId,
"clientId": clientId,
"pushApplicationId": pushAppId,
},
},
Data: map[string][]byte{
// Used to generate the name of the UI annotations
constants.BindingDataServiceInstanceNameKey: []byte(serviceInstanceName),
"config": []byte("{}"),
},
}
secret, err := helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).Create(&payload)
// TODO: remove error handling here!
if err != nil {
log.Fatal("Error creating ups config secret", err)
} else {
log.Printf("Config secret `%s` for variant created", configSecretName)
}
return secret
}
func (helper KubeHelperImpl) updateSecret(secret *v1.Secret) (*v1.Secret, error) {
return helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).Update(secret)
}
// Deletes a secret
func (helper KubeHelperImpl) deleteSecret(name string) {
err := helper.k8client.CoreV1().Secrets(os.Getenv(constants.EnvVarKeyNamespace)).Delete(name, nil)
// TODO: remove error handling here!
if err != nil {
log.Print("Error deleting secret", err)
} else {
log.Printf("Secret `%s` has been deleted", name)
}
}
// Create a random identifier of the given length. Useful for randomized resource names
func getRandomIdentifier(length int) string {
result := make([]rune, length)
for i := 0; i < length; i++ {
result[i] = letters[rand.Intn(len(letters))]
}
return string(result)
}