| package status |
| |
| import ( |
| "context" |
| "encoding/json" |
| "fmt" |
| |
| "github.com/giolekva/pcloud/core/installer/kube" |
| |
| "k8s.io/apimachinery/pkg/api/errors" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" |
| "k8s.io/apimachinery/pkg/runtime/schema" |
| "k8s.io/client-go/dynamic" |
| ) |
| |
| type ingressMonitor struct { |
| d dynamic.Interface |
| } |
| |
| type ingress struct { |
| APIVersion string `json:"apiVersion"` |
| Kind string `json:"kind"` |
| Metadata Metadata `json:"metadata"` |
| Spec struct { |
| TLS []struct { |
| SecretName string `json:"secretName"` |
| } `json:"tls"` |
| } `json:"spec"` |
| } |
| |
| type certificate struct { |
| APIVersion string `json:"apiVersion"` |
| Kind string `json:"kind"` |
| Metadata Metadata `json:"metadata"` |
| Spec struct { |
| SecretName string `json:"secretName"` |
| } `json:"spec"` |
| } |
| |
| func (c certificate) OwnerReferences() []OwnerReference { |
| return c.Metadata.OwnerReferences |
| } |
| |
| type certificateRequest struct { |
| Metadata Metadata `json:"metadata"` |
| Status struct { |
| Conditions []struct { |
| Type string `json:"type"` |
| Status string `json:"status"` |
| } `json:"conditions"` |
| } `json:"status"` |
| } |
| |
| func (c certificateRequest) OwnerReferences() []OwnerReference { |
| return c.Metadata.OwnerReferences |
| } |
| |
| func (c certificateRequest) IsReady() bool { |
| for _, cond := range c.Status.Conditions { |
| if cond.Type == "Ready" && cond.Status == "True" { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (m *ingressMonitor) Get(ref ResourceRef) (Status, error) { |
| ctx := context.Background() |
| res, err := m.d.Resource( |
| schema.GroupVersionResource{ |
| Group: "networking.k8s.io", |
| Version: "v1", |
| Resource: "ingresses", |
| }, |
| ).Namespace(ref.Namespace).Get(ctx, ref.Name, metav1.GetOptions{}) |
| if err != nil { |
| if errors.IsNotFound(err) { |
| return StatusNotFound, nil |
| } |
| return StatusNoStatus, err |
| } |
| b, err := res.MarshalJSON() |
| if err != nil { |
| return StatusNoStatus, err |
| } |
| var r ingress |
| if err := json.Unmarshal(b, &r); err != nil { |
| return StatusNoStatus, err |
| } |
| id, ok := r.Metadata.Annotations["dodo.cloud/id"] |
| if !ok { |
| fmt.Printf("## missing dodo.cloud/id: %+v\n", ref) |
| // TODO(gio): pass down annotations from helm release to resources |
| // return StatusNoStatus, nil |
| } else if id != ref.Id { |
| return StatusNotFound, nil |
| } |
| certs, err := decodeResources[certificate](m.d.Resource( |
| schema.GroupVersionResource{ |
| Group: "cert-manager.io", |
| Version: "v1", |
| Resource: "certificates", |
| }, |
| ).Namespace(ref.Namespace).List(ctx, metav1.ListOptions{})) |
| certs = filterByOwner(certs, OwnerReference{r.APIVersion, r.Kind, r.Metadata.Name}) |
| if err != nil { |
| return StatusNotFound, nil |
| } |
| certReqs, err := decodeResources[certificateRequest](m.d.Resource( |
| schema.GroupVersionResource{ |
| Group: "cert-manager.io", |
| Version: "v1", |
| Resource: "certificaterequests", |
| }, |
| ).Namespace(ref.Namespace).List(ctx, metav1.ListOptions{})) |
| if err != nil { |
| return StatusNotFound, nil |
| } |
| if len(r.Spec.TLS) != len(certs) { |
| return StatusProcessing, nil |
| } |
| for _, tls := range r.Spec.TLS { |
| var cert *certificate |
| for _, c := range certs { |
| if tls.SecretName == c.Spec.SecretName { |
| cert = &c |
| break |
| } |
| } |
| if cert == nil { |
| return StatusProcessing, nil |
| } |
| reqs := filterByOwner(certReqs, OwnerReference{cert.APIVersion, cert.Kind, cert.Metadata.Name}) |
| for _, req := range reqs { |
| if !req.IsReady() { |
| return StatusProcessing, nil |
| } |
| } |
| } |
| return StatusSuccess, nil |
| } |
| |
| func NewIngressMonitor(kubeconfig string) (ResourceMonitor, error) { |
| c, err := kube.NewKubeClient(kube.KubeConfigOpts{KubeConfigPath: kubeconfig}) |
| if err != nil { |
| return nil, err |
| } |
| d := dynamic.New(c.RESTClient()) |
| return &ingressMonitor{d}, nil |
| } |
| |
| func filterByOwner[T ResourceWithOwnerReferences](certs []T, owner OwnerReference) []T { |
| ret := []T{} |
| for _, i := range certs { |
| for _, o := range i.OwnerReferences() { |
| if owner.APIVersion == o.APIVersion && owner.Kind == o.Kind && owner.Name == o.Name { |
| ret = append(ret, i) |
| break |
| } |
| } |
| } |
| return ret |
| } |
| |
| func decodeResources[T any](list *unstructured.UnstructuredList, err error) ([]T, error) { |
| if err != nil { |
| return nil, err |
| } |
| ret := []T{} |
| for _, i := range list.Items { |
| b, err := i.MarshalJSON() |
| if err != nil { |
| return nil, err |
| } |
| var r T |
| if err := json.Unmarshal(b, &r); err != nil { |
| return nil, err |
| } |
| ret = append(ret, r) |
| } |
| return ret, nil |
| } |