blob: b76c8e0ece2107c6543acefa943c6b0a45c68205 [file] [log] [blame]
gioea6d9122025-05-22 17:57:18 +04001package status
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 "github.com/giolekva/pcloud/core/installer/kube"
9
10 "k8s.io/apimachinery/pkg/api/errors"
11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13 "k8s.io/apimachinery/pkg/runtime/schema"
14 "k8s.io/client-go/dynamic"
15)
16
17type ingressMonitor struct {
18 d dynamic.Interface
19}
20
21type ingress struct {
22 APIVersion string `json:"apiVersion"`
23 Kind string `json:"kind"`
24 Metadata Metadata `json:"metadata"`
25 Spec struct {
26 TLS []struct {
27 SecretName string `json:"secretName"`
28 } `json:"tls"`
29 } `json:"spec"`
30}
31
32type certificate struct {
33 APIVersion string `json:"apiVersion"`
34 Kind string `json:"kind"`
35 Metadata Metadata `json:"metadata"`
36 Spec struct {
37 SecretName string `json:"secretName"`
38 } `json:"spec"`
39}
40
41func (c certificate) OwnerReferences() []OwnerReference {
42 return c.Metadata.OwnerReferences
43}
44
45type certificateRequest struct {
46 Metadata Metadata `json:"metadata"`
47 Status struct {
48 Conditions []struct {
49 Type string `json:"type"`
50 Status string `json:"status"`
51 } `json:"conditions"`
52 } `json:"status"`
53}
54
55func (c certificateRequest) OwnerReferences() []OwnerReference {
56 return c.Metadata.OwnerReferences
57}
58
59func (c certificateRequest) IsReady() bool {
60 for _, cond := range c.Status.Conditions {
61 if cond.Type == "Ready" && cond.Status == "True" {
62 return true
63 }
64 }
65 return false
66}
67
68func (m *ingressMonitor) Get(ref ResourceRef) (Status, error) {
69 ctx := context.Background()
70 res, err := m.d.Resource(
71 schema.GroupVersionResource{
72 Group: "networking.k8s.io",
73 Version: "v1",
74 Resource: "ingresses",
75 },
76 ).Namespace(ref.Namespace).Get(ctx, ref.Name, metav1.GetOptions{})
77 if err != nil {
78 if errors.IsNotFound(err) {
79 return StatusNotFound, nil
80 }
81 return StatusNoStatus, err
82 }
83 b, err := res.MarshalJSON()
84 if err != nil {
85 return StatusNoStatus, err
86 }
87 var r ingress
88 if err := json.Unmarshal(b, &r); err != nil {
89 return StatusNoStatus, err
90 }
91 id, ok := r.Metadata.Annotations["dodo.cloud/id"]
92 if !ok {
93 fmt.Printf("## missing dodo.cloud/id: %+v\n", ref)
94 // TODO(gio): pass down annotations from helm release to resources
95 // return StatusNoStatus, nil
96 } else if id != ref.Id {
97 return StatusNotFound, nil
98 }
99 certs, err := decodeResources[certificate](m.d.Resource(
100 schema.GroupVersionResource{
101 Group: "cert-manager.io",
102 Version: "v1",
103 Resource: "certificates",
104 },
105 ).Namespace(ref.Namespace).List(ctx, metav1.ListOptions{}))
106 certs = filterByOwner(certs, OwnerReference{r.APIVersion, r.Kind, r.Metadata.Name})
107 if err != nil {
108 return StatusNotFound, nil
109 }
110 certReqs, err := decodeResources[certificateRequest](m.d.Resource(
111 schema.GroupVersionResource{
112 Group: "cert-manager.io",
113 Version: "v1",
114 Resource: "certificaterequests",
115 },
116 ).Namespace(ref.Namespace).List(ctx, metav1.ListOptions{}))
117 if err != nil {
118 return StatusNotFound, nil
119 }
120 if len(r.Spec.TLS) != len(certs) {
121 return StatusProcessing, nil
122 }
123 for _, tls := range r.Spec.TLS {
124 var cert *certificate
125 for _, c := range certs {
126 if tls.SecretName == c.Spec.SecretName {
127 cert = &c
128 break
129 }
130 }
131 if cert == nil {
132 return StatusProcessing, nil
133 }
134 reqs := filterByOwner(certReqs, OwnerReference{cert.APIVersion, cert.Kind, cert.Metadata.Name})
135 for _, req := range reqs {
136 if !req.IsReady() {
137 return StatusProcessing, nil
138 }
139 }
140 }
141 return StatusSuccess, nil
142}
143
144func NewIngressMonitor(kubeconfig string) (ResourceMonitor, error) {
145 c, err := kube.NewKubeClient(kube.KubeConfigOpts{KubeConfigPath: kubeconfig})
146 if err != nil {
147 return nil, err
148 }
149 d := dynamic.New(c.RESTClient())
150 return &ingressMonitor{d}, nil
151}
152
153func filterByOwner[T ResourceWithOwnerReferences](certs []T, owner OwnerReference) []T {
154 ret := []T{}
155 for _, i := range certs {
156 for _, o := range i.OwnerReferences() {
157 if owner.APIVersion == o.APIVersion && owner.Kind == o.Kind && owner.Name == o.Name {
158 ret = append(ret, i)
159 break
160 }
161 }
162 }
163 return ret
164}
165
166func decodeResources[T any](list *unstructured.UnstructuredList, err error) ([]T, error) {
167 if err != nil {
168 return nil, err
169 }
170 ret := []T{}
171 for _, i := range list.Items {
172 b, err := i.MarshalJSON()
173 if err != nil {
174 return nil, err
175 }
176 var r T
177 if err := json.Unmarshal(b, &r); err != nil {
178 return nil, err
179 }
180 ret = append(ret, r)
181 }
182 return ret, nil
183}