blob: b76c8e0ece2107c6543acefa943c6b0a45c68205 [file] [log] [blame]
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
}