Skip to content

Commit

Permalink
extend ActionInterface with a health checking interface
Browse files Browse the repository at this point in the history
  • Loading branch information
joelanford committed Apr 11, 2024
1 parent cfc667c commit 2300f1a
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 32 deletions.
14 changes: 8 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ require (
github.com/stretchr/testify v1.9.0
gomodules.xyz/jsonpatch/v2 v2.4.0
helm.sh/helm/v3 v3.14.3
k8s.io/api v0.29.2
k8s.io/api v0.29.3
k8s.io/apiextensions-apiserver v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/apimachinery v0.29.3
k8s.io/cli-runtime v0.29.2
k8s.io/client-go v0.29.2
k8s.io/client-go v0.29.3
k8s.io/kube-aggregator v0.29.3
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/cli-utils v0.35.0
sigs.k8s.io/controller-runtime v0.17.2
sigs.k8s.io/kubebuilder/v3 v3.14.0
sigs.k8s.io/yaml v1.4.0
Expand Down Expand Up @@ -86,7 +88,7 @@ require (
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gomodule/redigo v1.8.2 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
Expand Down Expand Up @@ -175,8 +177,8 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiserver v0.29.2 // indirect
k8s.io/component-base v0.29.2 // indirect
k8s.io/apiserver v0.29.3 // indirect
k8s.io/component-base v0.29.3 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/kubectl v0.29.2 // indirect
Expand Down
32 changes: 16 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
Expand All @@ -184,7 +183,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
Expand Down Expand Up @@ -556,8 +554,6 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
Expand All @@ -582,22 +578,24 @@ helm.sh/helm/v3 v3.14.3 h1:HmvRJlwyyt9HjgmAuxHbHv3PhMz9ir/XNWHyXfmnOP4=
helm.sh/helm/v3 v3.14.3/go.mod h1:v6myVbyseSBJTzhmeE39UcPLNv6cQK6qss3dvgAySaE=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg=
k8s.io/apiextensions-apiserver v0.29.2/go.mod h1:aLfYjpA5p3OwtqNXQFkhJ56TB+spV8Gc4wfMhUA3/b8=
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/apiserver v0.29.2 h1:+Z9S0dSNr+CjnVXQePG8TcBWHr3Q7BmAr7NraHvsMiQ=
k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ=
k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
k8s.io/apiserver v0.29.3 h1:xR7ELlJ/BZSr2n4CnD3lfA4gzFivh0wwfNfz9L0WZcE=
k8s.io/apiserver v0.29.3/go.mod h1:hrvXlwfRulbMbBgmWRQlFru2b/JySDpmzvQwwk4GUOs=
k8s.io/cli-runtime v0.29.2 h1:smfsOcT4QujeghsNjECKN3lwyX9AwcFU0nvJ7sFN3ro=
k8s.io/cli-runtime v0.29.2/go.mod h1:KLisYYfoqeNfO+MkTWvpqIyb1wpJmmFJhioA0xd4MW8=
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8=
k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM=
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
k8s.io/kube-aggregator v0.29.3 h1:5KvTyFN8sQq2imq8tMAHWEKoE64Zg9WSMaGX78KV6ps=
k8s.io/kube-aggregator v0.29.3/go.mod h1:xGJqV/SJJ1fbwTGfQLAZfwgqX1EMoaqfotDTkDrqqSk=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo=
Expand All @@ -606,6 +604,8 @@ k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSn
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
sigs.k8s.io/cli-utils v0.35.0 h1:dfSJaF1W0frW74PtjwiyoB4cwdRygbHnC7qe7HF0g/Y=
sigs.k8s.io/cli-utils v0.35.0/go.mod h1:ITitykCJxP1vaj1Cew/FZEaVJ2YsTN9Q71m02jebkoE=
sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0=
sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
Expand Down
87 changes: 87 additions & 0 deletions pkg/client/actionclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package client

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
Expand All @@ -32,10 +33,13 @@ import (
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apierrors "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"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/cli-runtime/pkg/resource"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -55,6 +59,7 @@ type ActionInterface interface {
Upgrade(name, namespace string, chrt *chart.Chart, vals map[string]interface{}, opts ...UpgradeOption) (*release.Release, error)
Uninstall(name string, opts ...UninstallOption) (*release.UninstallReleaseResponse, error)
Reconcile(rel *release.Release) error
CheckHealth(ctx context.Context, rel *release.Release) error
}

type GetOption func(*action.Get) error
Expand Down Expand Up @@ -329,6 +334,88 @@ func (c *actionClient) Reconcile(rel *release.Release) error {
})
}

func (c *actionClient) CheckHealth(ctx context.Context, rel *release.Release) error {
var gvkErrors []error
infos, err := c.conf.KubeClient.Build(bytes.NewBufferString(rel.Manifest), false)
if err != nil {
return err
}

return infos.Visit(func(info *resource.Info, err error) error {

Check warning on line 344 in pkg/client/actionclient.go

View workflow job for this annotation

GitHub Actions / Lint

unreachable-code: unreachable code after this statement (revive)
if err != nil {
return fmt.Errorf("visit error: %w", err)
}

u := &unstructured.Unstructured{}
u.SetGroupVersionKind(info.Object.GetObjectKind().GroupVersionKind())
u.SetNamespace(info.Namespace)
u.SetName(info.Name)

helper := resource.NewHelper(info.Client, info.Mapping)
req := helper.RESTClient.Get().
NamespaceIfScoped(info.Namespace, helper.NamespaceScoped).
Resource(helper.Resource).
Name(info.Namespace).
SubResource(helper.Subresource)
if err := req.Do(ctx).Into(u); err != nil {
gvkErrors = appendResourceError(gvkErrors, u, err.Error())
return nil
}

if u.GetObjectKind().GroupVersionKind() == apiregistrationv1.SchemeGroupVersion.WithKind("APIService") {
obj := &apiregistrationv1.APIService{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj); err != nil {
gvkErrors = appendResourceError(gvkErrors, obj, err.Error())
return nil
}

// Check if the APIService is available.
var isAvailable *apiregistrationv1.APIServiceCondition
for i, condition := range obj.Status.Conditions {
if condition.Type == apiregistrationv1.Available {
isAvailable = &obj.Status.Conditions[i]
break
}
}
if isAvailable == nil {
gvkErrors = appendResourceError(gvkErrors, obj, "Available condition not found")
} else if isAvailable.Status == apiregistrationv1.ConditionFalse {
gvkErrors = appendResourceError(gvkErrors, obj, isAvailable.Message)
}
return nil
}

result, err := status.Compute(u)
if err != nil {
gvkErrors = appendResourceError(gvkErrors, u, err.Error())
return nil
}

if result.Status != status.CurrentStatus {
gvkErrors = appendResourceError(gvkErrors, u, fmt.Sprintf("object %s: %s", result.Status, result.Message))
return nil
}
return nil
})
return errors.Join(gvkErrors...)

Check failure on line 400 in pkg/client/actionclient.go

View workflow job for this annotation

GitHub Actions / Lint

unreachable: unreachable code (govet)
}

// toErrKey returns a string that identifies a resource based on its GVK and namespace/name. This key is used
// to identify the resource in the error message.
func toErrKey(resource client.Object) string {
// If the resource is namespaced, include the namespace in the key.
if resource.GetNamespace() != "" {
return fmt.Sprintf("(%s)(%s/%s)", resource.GetObjectKind().GroupVersionKind().String(), resource.GetNamespace(), resource.GetName())
}

return fmt.Sprintf("(%s)(%s)", resource.GetObjectKind().GroupVersionKind().String(), resource.GetName())
}

// appendResourceError appends a new error to the given slice of errors and returns it.
func appendResourceError(gvkErrors []error, resource client.Object, message string) []error {
return append(gvkErrors, errors.New(toErrKey(resource)+": "+message))
}

func createPatch(existing runtime.Object, expected *resource.Info) ([]byte, apitypes.PatchType, error) {
existingJSON, err := json.Marshal(existing)
if err != nil {
Expand Down
32 changes: 22 additions & 10 deletions pkg/reconciler/internal/fake/actionclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package fake

import (
"context"
"errors"

"helm.sh/helm/v3/pkg/chart"
Expand Down Expand Up @@ -48,11 +49,12 @@ func (hcg *fakeActionClientGetter) ActionClientFor(_ crclient.Object) (client.Ac
}

type ActionClient struct {
Gets []GetCall
Installs []InstallCall
Upgrades []UpgradeCall
Uninstalls []UninstallCall
Reconciles []ReconcileCall
Gets []GetCall
Installs []InstallCall
Upgrades []UpgradeCall
Uninstalls []UninstallCall
Reconciles []ReconcileCall
CheckHealths []CheckHealthCall

HandleGet func() (*release.Release, error)
HandleInstall func() (*release.Release, error)
Expand All @@ -72,11 +74,12 @@ func NewActionClient() ActionClient {
return func() error { return err }
}
return ActionClient{
Gets: make([]GetCall, 0),
Installs: make([]InstallCall, 0),
Upgrades: make([]UpgradeCall, 0),
Uninstalls: make([]UninstallCall, 0),
Reconciles: make([]ReconcileCall, 0),
Gets: make([]GetCall, 0),
Installs: make([]InstallCall, 0),
Upgrades: make([]UpgradeCall, 0),
Uninstalls: make([]UninstallCall, 0),
Reconciles: make([]ReconcileCall, 0),
CheckHealths: make([]CheckHealthCall, 0),

HandleGet: relFunc(errors.New("get not implemented")),
HandleInstall: relFunc(errors.New("install not implemented")),
Expand Down Expand Up @@ -118,6 +121,10 @@ type ReconcileCall struct {
Release *release.Release
}

type CheckHealthCall struct {
Release *release.Release
}

func (c *ActionClient) Get(name string, opts ...client.GetOption) (*release.Release, error) {
c.Gets = append(c.Gets, GetCall{name, opts})
return c.HandleGet()
Expand All @@ -142,3 +149,8 @@ func (c *ActionClient) Reconcile(rel *release.Release) error {
c.Reconciles = append(c.Reconciles, ReconcileCall{rel})
return c.HandleReconcile()
}

func (c *ActionClient) CheckHealth(_ context.Context, rel *release.Release) error {
c.CheckHealths = append(c.CheckHealths, CheckHealthCall{rel})
return nil
}

0 comments on commit 2300f1a

Please sign in to comment.