Skip to content

Commit

Permalink
feat!: Add valid error in status field of GitServer (#15)
Browse files Browse the repository at this point in the history
Removed all unnecessary properties from status as they were not used. Added "error" property to the status for storing errors if the connection wasn't established.

BREAKING CHANGE: removed required fields from the GitServer status. CRD should be updated.

Change-Id: If12029a5e9940f6f439e2face5d4905b6eef2e09
  • Loading branch information
zmotso committed Sep 26, 2023
1 parent 1423a75 commit 67ed1e3
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 322 deletions.
32 changes: 6 additions & 26 deletions api/v1/git_server_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,40 +38,20 @@ type GitServerSpec struct {

// GitServerStatus defines the observed state of GitServer.
type GitServerStatus struct {
// This flag indicates neither JiraServer are initialized and ready to work. Defaults to false.
Available bool `json:"available"`

// Information when the last time the action were performed.
LastTimeUpdated metaV1.Time `json:"last_time_updated"`

// Specifies a current status of GitServer.
Status string `json:"status"`

// Name of user who made a last change.
Username string `json:"username"`

// The last Action was performed.
Action string `json:"action"`

// A result of an action which were performed.
// - "success": action where performed successfully;
// - "error": error has occurred;
Result string `json:"result"`

// Detailed information regarding action result
// which were performed
// Error represents error message if something went wrong.
// +optional
DetailedMessage string `json:"detailed_message,omitempty"`
Error string `json:"error,omitempty"`

// Specifies a current state of GitServer.
Value string `json:"value"`
// Connected shows if operator is connected to git server.
// +optional
Connected bool `json:"connected"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=gs
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:name="Available",type="boolean",JSONPath=".status.available",description="Is resource available"
// +kubebuilder:printcolumn:name="Connected",type="boolean",JSONPath=".status.connected",description="Is connected to git server"
// +kubebuilder:printcolumn:name="Host",type="string",JSONPath=".spec.gitHost",description="GitSever host"
// +kubebuilder:printcolumn:name="Git Provider",type="string",JSONPath=".spec.gitProvider",description="Git Provider type"

Expand Down
3 changes: 1 addition & 2 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 7 additions & 37 deletions config/crd/bases/v2.edp.epam.com_gitservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
- description: Is resource available
jsonPath: .status.available
name: Available
- description: Is connected to git server
jsonPath: .status.connected
name: Connected
type: boolean
- description: GitSever host
jsonPath: .spec.gitHost
Expand Down Expand Up @@ -86,42 +86,12 @@ spec:
status:
description: GitServerStatus defines the observed state of GitServer.
properties:
action:
description: The last Action was performed.
type: string
available:
description: This flag indicates neither JiraServer are initialized
and ready to work. Defaults to false.
connected:
description: Connected shows if operator is connected to git server.
type: boolean
detailed_message:
description: Detailed information regarding action result which were
performed
type: string
last_time_updated:
description: Information when the last time the action were performed.
format: date-time
type: string
result:
description: 'A result of an action which were performed. - "success":
action where performed successfully; - "error": error has occurred;'
error:
description: Error represents error message if something went wrong.
type: string
status:
description: Specifies a current status of GitServer.
type: string
username:
description: Name of user who made a last change.
type: string
value:
description: Specifies a current state of GitServer.
type: string
required:
- action
- available
- last_time_updated
- result
- status
- username
- value
type: object
type: object
served: true
Expand Down
8 changes: 8 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ metadata:
name: manager-role
namespace: placeholder
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- argoproj.io
resources:
Expand Down
123 changes: 52 additions & 71 deletions controllers/gitserver/gitserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,39 @@ import (
"fmt"
"time"

"github.com/go-logr/logr"
coreV1 "k8s.io/api/core/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

codebaseApi "github.com/epam/edp-codebase-operator/v2/api/v1"
"github.com/epam/edp-codebase-operator/v2/pkg/model"
codebasepredicate "github.com/epam/edp-codebase-operator/v2/pkg/predicate"
)

func NewReconcileGitServer(c client.Client, log logr.Logger) *ReconcileGitServer {
const (
defaultRequeueTime = time.Second * 30
successRequeueTime = time.Minute * 30
)

func NewReconcileGitServer(c client.Client) *ReconcileGitServer {
return &ReconcileGitServer{
client: c,
log: log.WithName("git-server"),
}
}

type ReconcileGitServer struct {
client client.Client
log logr.Logger
}

func (r *ReconcileGitServer) SetupWithManager(mgr ctrl.Manager) error {
p := predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
oldObject, ok := e.ObjectOld.(*codebaseApi.GitServer)
if !ok {
return false
}

newObject, ok := e.ObjectNew.(*codebaseApi.GitServer)
if !ok {
return false
}

if codebasepredicate.PauseAnnotationChanged(oldObject, newObject) {
return true
}

return oldObject.Status == newObject.Status
},
}

pause := codebasepredicate.NewPause(r.log)
pause := codebasepredicate.NewPause(ctrl.Log.WithName("git-server-pause-predicate"))

err := ctrl.NewControllerManagedBy(mgr).
For(&codebaseApi.GitServer{}, builder.WithPredicates(pause, p)).
For(&codebaseApi.GitServer{}, builder.WithPredicates(pause)).
Complete(r)
if err != nil {
return fmt.Errorf("failed to build GitServer controller: %w", err)
Expand All @@ -68,87 +49,87 @@ func (r *ReconcileGitServer) SetupWithManager(mgr ctrl.Manager) error {
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gitservers,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gitservers/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=v2.edp.epam.com,namespace=placeholder,resources=gitservers/finalizers,verbs=update
//+kubebuilder:rbac:groups="",namespace=placeholder,resources=secrets,verbs=get;list;watch

// Reconcile reads that state of the cluster for a GitServer object and makes changes based on the state.
func (r *ReconcileGitServer) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
log := ctrl.LoggerFrom(ctx)
log.Info("Reconciling GitServer")

instance := &codebaseApi.GitServer{}

err := r.client.Get(ctx, request.NamespacedName, instance)
if err != nil {
if err := r.client.Get(ctx, request.NamespacedName, instance); err != nil {
if k8sErrors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
// Return and don't requeue
return reconcile.Result{}, nil
}

return reconcile.Result{}, fmt.Errorf("failed to fetch resource %q: %w", request.NamespacedName, err)
}

oldStatus := instance.Status
gitServer := model.ConvertToGitServer(instance)

hasConnection, err := checkConnectionToGitServer(r.client, gitServer, log)
if err != nil {
if updateErr := r.updateStatus(ctx, r.client, instance, hasConnection); updateErr != nil {
log.Error(updateErr, "failed to update GitServer status")
if err := r.checkConnectionToGitServer(ctx, gitServer); err != nil {
instance.Status.Error = err.Error()
instance.Status.Connected = false

if statusErr := r.updateGitServerStatus(ctx, instance, oldStatus); statusErr != nil {
return reconcile.Result{}, statusErr
}

return reconcile.Result{}, fmt.Errorf("failed to check connection to Git Server %v: %w", gitServer.GitHost, err)
}
log.Error(err, "GitServer connection is not established")

if err := r.updateStatus(ctx, r.client, instance, hasConnection); err != nil {
return reconcile.Result{}, fmt.Errorf("failed to update GitServer status %v: %w", gitServer.GitHost, err)
return reconcile.Result{RequeueAfter: defaultRequeueTime}, nil
}

if !hasConnection {
const requeueTime = 30 * time.Second
instance.Status.Error = ""
instance.Status.Connected = true

log.Info("GitServer does not have connection, will try again later")

return reconcile.Result{RequeueAfter: requeueTime}, nil
if err := r.updateGitServerStatus(ctx, instance, oldStatus); err != nil {
return reconcile.Result{}, err
}

log.Info("Reconciling GitServer has been finished")

return reconcile.Result{}, nil
return reconcile.Result{
RequeueAfter: successRequeueTime,
}, nil
}

func (*ReconcileGitServer) updateStatus(ctx context.Context, c client.Client, instance *codebaseApi.GitServer, hasConnection bool) error {
func (r *ReconcileGitServer) checkConnectionToGitServer(ctx context.Context, gitServer *model.GitServer) error {
log := ctrl.LoggerFrom(ctx)
log.Info("Start CheckConnectionToGitServer method", "host", gitServer.GitHost)

instance.Status = generateStatus(hasConnection)
sshSecret := &coreV1.Secret{}

err := c.Status().Update(ctx, instance)
err := r.client.Get(ctx, types.NamespacedName{
Namespace: gitServer.Namespace,
Name: gitServer.NameSshKeySecret,
}, sshSecret)
if err != nil {
_ = c.Update(ctx, instance)
return fmt.Errorf("failed to get secret %s: %w", gitServer.NameSshKeySecret, err)
}

sshData := extractSshData(gitServer, sshSecret)

log.Info("Data from request is extracted", "host", sshData.Host, "port", sshData.Port)

if err = checkGitServerConnection(sshData, log); err != nil {
return fmt.Errorf("failed to establish connection to Git Server %s: %w", sshData.Host, err)
}

log.Info("Status for GitServer is set up.")
log.Info("Git server connection is established", "host", sshData.Host)

return nil
}

func generateStatus(hasConnection bool) codebaseApi.GitServerStatus {
if hasConnection {
return codebaseApi.GitServerStatus{
Status: "created",
Available: hasConnection,
LastTimeUpdated: metaV1.Now(),
Result: "success",
Username: "system",
Value: "active",
}
func (r *ReconcileGitServer) updateGitServerStatus(ctx context.Context, gitServer *codebaseApi.GitServer, oldStatus codebaseApi.GitServerStatus) error {
if gitServer.Status == oldStatus {
return nil
}

return codebaseApi.GitServerStatus{
Status: "created",
Available: hasConnection,
LastTimeUpdated: metaV1.Now(),
Result: "error",
Username: "system",
Value: "inactive",
if err := r.client.Status().Update(ctx, gitServer); err != nil {
return fmt.Errorf("failed to update GitServer status: %w", err)
}

return nil
}
Loading

0 comments on commit 67ed1e3

Please sign in to comment.