-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
193 lines (168 loc) · 5.17 KB
/
main.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
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
func main() {
// Retrieve the namespace and resource name from command-line arguments
namespace := flag.String("namespace", "", "Namespace of the ExternalSecret")
name := flag.String("name", "", "Name of the ExternalSecret")
flag.Parse()
if *namespace == "" || *name == "" {
fmt.Println("Usage: ./external-secret-watcher -namespace=<namespace> -name=<name>")
os.Exit(1)
}
// Read KUBECONFIG from environment variables or from the home directory
kubeconfigEnv := os.Getenv("KUBECONFIG")
var kubeconfig string
if kubeconfigEnv != "" {
kubeconfig = kubeconfigEnv
} else if home := homedir.HomeDir(); home != "" {
kubeconfig = filepath.Join(home, ".kube", "config")
}
var config *rest.Config
var err error
if kubeconfig != "" {
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
} else {
config, err = rest.InClusterConfig()
}
if err != nil {
fmt.Printf("Error building kubeconfig: %v\n", err)
os.Exit(1)
}
// Create client sets
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Printf("Error creating Kubernetes clientset: %v\n", err)
os.Exit(1)
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
fmt.Printf("Error creating dynamic client: %v\n", err)
os.Exit(1)
}
// Start watching events in a separate goroutine
go watchEvents(clientset, *namespace, *name)
// Check the status of the ExternalSecret with timeout
timeout := 10 * time.Minute
err = checkStatusWithTimeout(dynamicClient, *namespace, *name, timeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
}
func watchEvents(clientset *kubernetes.Clientset, namespace, name string) {
fmt.Printf("Watching events for ExternalSecret %s in namespace %s...\n", name, namespace)
fieldSelector := fields.AndSelectors(
fields.OneTermEqualSelector("involvedObject.kind", "ExternalSecret"),
fields.OneTermEqualSelector("involvedObject.name", name),
).String()
listOptions := metav1.ListOptions{
FieldSelector: fieldSelector,
}
for {
watcher, err := clientset.CoreV1().Events(namespace).Watch(context.TODO(), listOptions)
if err != nil {
fmt.Printf("Error watching events: %v\n", err)
time.Sleep(5 * time.Second)
continue
}
for event := range watcher.ResultChan() {
if e, ok := event.Object.(*corev1.Event); ok {
fmt.Printf("Event: %s - %s: %s\n", e.LastTimestamp, e.Reason, e.Message)
}
}
}
}
func checkStatusWithTimeout(dynamicClient dynamic.Interface, namespace, name string, timeout time.Duration) error {
// Define the GroupVersionResource for ExternalSecret
externalSecretGVR := schema.GroupVersionResource{
Group: "external-secrets.io",
Version: "v1beta1",
Resource: "externalsecrets",
}
// Create a context with timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return fmt.Errorf("timeout reached: ExternalSecret %s did not become Ready within %v", name, timeout)
case <-ticker.C:
// Get the ExternalSecret resource
unstructuredES, err := dynamicClient.Resource(externalSecretGVR).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
fmt.Printf("Error getting ExternalSecret: %v\n", err)
} else {
if isReady(unstructuredES) {
fmt.Printf("ExternalSecret %s has reached Ready state.\n", name)
return nil
} else {
fmt.Printf("Waiting... Current status conditions: %v\n", getConditions(unstructuredES))
}
}
}
}
}
func isReady(unstructuredES *unstructured.Unstructured) bool {
conditions := getConditions(unstructuredES)
for _, condition := range conditions {
if condition.Type == "Ready" && condition.Status == "True" {
return true
}
}
return false
}
type Condition struct {
Type string `json:"type"`
Status string `json:"status"`
LastTransitionTime string `json:"lastTransitionTime"`
Reason string `json:"reason"`
Message string `json:"message"`
}
func getConditions(unstructuredES *unstructured.Unstructured) []Condition {
status, found, err := unstructured.NestedMap(unstructuredES.Object, "status")
if !found || err != nil {
return []Condition{}
}
conditionsInterface, found, err := unstructured.NestedSlice(status, "conditions")
if !found || err != nil {
return []Condition{}
}
var conditions []Condition
for _, c := range conditionsInterface {
conditionMap, ok := c.(map[string]interface{})
if !ok {
continue
}
conditionBytes, err := json.Marshal(conditionMap)
if err != nil {
continue
}
var condition Condition
err = json.Unmarshal(conditionBytes, &condition)
if err != nil {
continue
}
conditions = append(conditions, condition)
}
return conditions
}