Skip to content

Commit

Permalink
fix: resources should be synced after k8sgptConfig CR update (#174)
Browse files Browse the repository at this point in the history
Signed-off-by: Yuan Fang <yuanfang@alauda.io>
Signed-off-by: Alex Jones <alexsimonjones@gmail.com>
Co-authored-by: Alex Jones <alexsimonjones@gmail.com>
  • Loading branch information
fyuan1316 and AlexsJones authored Jul 27, 2023
1 parent d356f14 commit 080a2b1
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 51 deletions.
15 changes: 8 additions & 7 deletions controllers/k8sgpt_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (r *K8sGPTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
if utils.ContainsString(k8sgptConfig.GetFinalizers(), FinalizerName) {

// Delete any external resources associated with the instance
err := resources.Sync(ctx, r.Client, *k8sgptConfig, resources.Destroy)
err := resources.Sync(ctx, r.Client, *k8sgptConfig, resources.DestroyOp)
if err != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
Expand All @@ -125,13 +125,14 @@ func (r *K8sGPTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
deployment := v1.Deployment{}
err = r.Get(ctx, client.ObjectKey{Namespace: k8sgptConfig.Namespace,
Name: "k8sgpt-deployment"}, &deployment)
if client.IgnoreNotFound(err) != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
}
err = resources.Sync(ctx, r.Client, *k8sgptConfig, resources.SyncOp)
if err != nil {

err = resources.Sync(ctx, r.Client, *k8sgptConfig, resources.Create)
if err != nil {
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
}
k8sgptReconcileErrorCount.Inc()
return r.finishReconcile(err, false)
}

if deployment.Status.ReadyReplicas > 0 {
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ require (
k8s.io/cli-runtime v0.27.4
k8s.io/client-go v0.27.4
k8s.io/kubectl v0.27.4
k8s.io/utils v0.0.0-20230313181309-38a27ef9d749
sigs.k8s.io/controller-runtime v0.15.0
)

require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/stretchr/testify v1.8.2
golang.org/x/tools v0.9.3 // indirect
)

Expand Down Expand Up @@ -68,6 +69,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
Expand Down Expand Up @@ -96,7 +98,6 @@ require (
k8s.io/component-base v0.27.4 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect
Expand Down
118 changes: 76 additions & 42 deletions pkg/resources/k8sgpt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,30 @@ import (
"github.com/k8sgpt-ai/k8sgpt-operator/api/v1alpha1"
"github.com/k8sgpt-ai/k8sgpt-operator/pkg/utils"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
r1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

// enum create or destroy
type CreateOrDestroy int
// SyncOrDestroy enum create or destroy
type SyncOrDestroy int

const (
Create CreateOrDestroy = iota
Destroy
SyncOp SyncOrDestroy = iota
DestroyOp
DeploymentName = "k8sgpt-deployment"
)

// GetService Create service for K8sGPT
func GetService(config v1alpha1.K8sGPT) (*v1.Service, error) {

func GetService(config v1alpha1.K8sGPT) (*corev1.Service, error) {
// Create service
service := v1.Service{
service := corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "k8sgpt",
Namespace: config.Namespace,
Expand All @@ -58,11 +59,11 @@ func GetService(config v1alpha1.K8sGPT) (*v1.Service, error) {
},
},
},
Spec: v1.ServiceSpec{
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": DeploymentName,
},
Ports: []v1.ServicePort{
Ports: []corev1.ServicePort{
{
Port: 8080,
},
Expand All @@ -73,11 +74,10 @@ func GetService(config v1alpha1.K8sGPT) (*v1.Service, error) {
return &service, nil
}

// GetServiceAccount Create a Service Account for K8sGPT and bind it to K8sGPT role
func GetServiceAccount(config v1alpha1.K8sGPT) (*v1.ServiceAccount, error) {

// GetServiceAccount Create Service Account for K8sGPT and bind it to K8sGPT role
func GetServiceAccount(config v1alpha1.K8sGPT) (*corev1.ServiceAccount, error) {
// Create service account
serviceAccount := v1.ServiceAccount{
serviceAccount := corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "k8sgpt",
Namespace: config.Namespace,
Expand Down Expand Up @@ -189,23 +189,23 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
"app": DeploymentName,
},
},
Template: v1.PodTemplateSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": DeploymentName,
},
},
Spec: v1.PodSpec{
Spec: corev1.PodSpec{
ServiceAccountName: "k8sgpt",
Containers: []v1.Container{
Containers: []corev1.Container{
{
Name: "k8sgpt",
ImagePullPolicy: v1.PullAlways,
ImagePullPolicy: corev1.PullAlways,
Image: "ghcr.io/k8sgpt-ai/k8sgpt:" + config.Spec.Version,
Args: []string{
"serve",
},
Env: []v1.EnvVar{
Env: []corev1.EnvVar{
{
Name: "K8SGPT_MODEL",
Value: config.Spec.AI.Model,
Expand All @@ -223,32 +223,32 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
Value: "/k8sgpt-data/.cache",
},
},
Ports: []v1.ContainerPort{
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceMemory: resource.MustParse("512Mi"),
Resources: corev1.ResourceRequirements{
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("512Mi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("0.2"),
v1.ResourceMemory: resource.MustParse("156Mi"),
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("0.2"),
corev1.ResourceMemory: resource.MustParse("156Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
VolumeMounts: []corev1.VolumeMount{
{
MountPath: "/k8sgpt-data",
Name: "k8sgpt-vol",
},
},
},
},
Volumes: []v1.Volume{
Volumes: []corev1.Volume{
{
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
Name: "k8sgpt-vol",
},
},
Expand All @@ -257,11 +257,11 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
},
}
if config.Spec.AI.Secret != nil {
password := v1.EnvVar{
password := corev1.EnvVar{
Name: "K8SGPT_PASSWORD",
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: config.Spec.AI.Secret.Name,
},
Key: config.Spec.AI.Secret.Key,
Expand All @@ -273,7 +273,7 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
)
}
if config.Spec.AI.BaseUrl != "" {
baseUrl := v1.EnvVar{
baseUrl := corev1.EnvVar{
Name: "K8SGPT_BASEURL",
Value: config.Spec.AI.BaseUrl,
}
Expand All @@ -283,7 +283,7 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
}
// Engine is required only when azureopenai is the ai backend
if config.Spec.AI.Engine != "" && config.Spec.AI.Backend == v1alpha1.AzureOpenAI {
engine := v1.EnvVar{
engine := corev1.EnvVar{
Name: "K8SGPT_ENGINE",
Value: config.Spec.AI.Engine,
}
Expand All @@ -297,7 +297,7 @@ func GetDeployment(config v1alpha1.K8sGPT) (*appsv1.Deployment, error) {
}

func Sync(ctx context.Context, c client.Client,
config v1alpha1.K8sGPT, i CreateOrDestroy) error {
config v1alpha1.K8sGPT, i SyncOrDestroy) error {

var objs []client.Object

Expand Down Expand Up @@ -339,27 +339,27 @@ func Sync(ctx context.Context, c client.Client,
// for each object, create or destroy
for _, obj := range objs {
switch i {
case Create:
case SyncOp:

// before creation we will check to see if the secret exists if used as a ref
// before creation, we will check to see if the secret exists if used as a ref
if config.Spec.AI.Secret != nil {

secret := &v1.Secret{}
secret := &corev1.Secret{}
er := c.Get(ctx, types.NamespacedName{Name: config.Spec.AI.Secret.Name,
Namespace: config.Namespace}, secret)
if er != nil {
return err.New("references secret does not exist, cannot create deployment")
}
}

err := c.Create(ctx, obj)
err := doSync(ctx, c, obj)
if err != nil {
// If the object already exists, ignore the error
if !errors.IsAlreadyExists(err) {
return err
}
}
case Destroy:
case DestroyOp:
err := c.Delete(ctx, obj)
if err != nil {
// if the object is not found, ignore the error
Expand All @@ -372,3 +372,37 @@ func Sync(ctx context.Context, c client.Client,

return nil
}

func doSync(ctx context.Context, clt client.Client, obj client.Object) error {
var mutateFn controllerutil.MutateFn
switch expect := obj.(type) {
case *appsv1.Deployment:
exist := &appsv1.Deployment{}
err := clt.Get(context.Background(), client.ObjectKeyFromObject(obj), exist)
if err != nil && !errors.IsNotFound(err) {
return err
} else if err == nil {
mutateFn = func() error {
exist.Spec = expect.Spec
return nil
}
obj = exist
}
case *corev1.Service:
exist := &corev1.Service{}
err := clt.Get(context.Background(), client.ObjectKeyFromObject(obj), exist)
if err != nil && !errors.IsNotFound(err) {
return err
} else if err == nil {
mutateFn = func() error {
exist.Spec = expect.Spec
return nil
}
obj = exist
}
}
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
_, err := controllerutil.CreateOrPatch(ctx, clt, obj, mutateFn)
return err
})
}
Loading

0 comments on commit 080a2b1

Please sign in to comment.