-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation of K8s-Deploy-Watcher with unit tests and GitH…
…ub Actions - Implemented K8s-Deploy-Watcher using Kubernetes Operator SDK. - Added CRD definition and Reconciler logic to track Deployment status. - Included unit tests using controller-runtime's fake client for local testing without Kubernetes. - Set up GitHub Actions workflow for automated unit testing. - Created Dockerfile for building and running the operator in a containerized environment. Signed-off-by: ddukbg <[email protected]>
- Loading branch information
Showing
10 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
name: Go CI | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Check out the repository | ||
uses: actions/checkout@v2 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v3 | ||
with: | ||
go-version: '1.16' | ||
|
||
- name: Install dependencies | ||
run: go mod download | ||
|
||
- name: Run tests | ||
run: go test ./... -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Dockerfile | ||
FROM golang:1.16 as builder | ||
WORKDIR /workspace | ||
COPY go.mod . | ||
COPY go.sum . | ||
RUN go mod download | ||
COPY . . | ||
RUN CGO_ENABLED=0 GOOS=linux go build -o manager main.go | ||
|
||
FROM gcr.io/distroless/static:nonroot | ||
WORKDIR / | ||
COPY --from=builder /workspace/manager . | ||
USER 65532:65532 | ||
ENTRYPOINT ["/manager"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Makefile | ||
|
||
# 빌드 | ||
build: | ||
docker build -t k8s-deploy-watcher . | ||
|
||
# 유닛 테스트 실행 | ||
test: | ||
go test ./controllers/... -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// DeploymentTrackerSpec defines the desired state of DeploymentTracker | ||
type DeploymentTrackerSpec struct { | ||
DeploymentName string `json:"deploymentName,omitempty"` | ||
Namespace string `json:"namespace,omitempty"` | ||
Notify Notify `json:"notify"` | ||
} | ||
|
||
// Notify defines notification details (Slack or Email) | ||
type Notify struct { | ||
Slack string `json:"slack,omitempty"` | ||
Email string `json:"email,omitempty"` | ||
} | ||
|
||
// DeploymentTrackerStatus defines the observed state of DeploymentTracker | ||
type DeploymentTrackerStatus struct { | ||
Ready bool `json:"ready,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// DeploymentTracker is the Schema for the deploymenttrackers API | ||
type DeploymentTracker struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec DeploymentTrackerSpec `json:"spec,omitempty"` | ||
Status DeploymentTrackerStatus `json:"status,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// DeploymentTrackerList contains a list of DeploymentTracker | ||
type DeploymentTrackerList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []DeploymentTracker `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&DeploymentTracker{}, &DeploymentTrackerList{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package v1alpha1 | ||
|
||
import ( | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"sigs.k8s.io/controller-runtime/pkg/scheme" | ||
) | ||
|
||
// GroupVersion is group version used to register these objects | ||
var GroupVersion = scheme.GroupVersion{ | ||
Group: "ddukbg", | ||
Version: "v1alpha1", | ||
} | ||
|
||
// SchemeBuilder adds the scheme to the manager | ||
var SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
apiVersion: apiextensions.k8s.io/v1 | ||
kind: CustomResourceDefinition | ||
metadata: | ||
name: deploymenttrackers.ddukbg | ||
spec: | ||
group: ddukbg | ||
names: | ||
kind: DeploymentTracker | ||
plural: deploymenttrackers | ||
scope: Namespaced | ||
versions: | ||
- name: v1alpha1 | ||
served: true | ||
storage: true | ||
schema: | ||
openAPIV3Schema: | ||
type: object | ||
properties: | ||
spec: | ||
type: object | ||
properties: | ||
deploymentName: | ||
type: string | ||
namespace: | ||
type: string | ||
notify: | ||
type: object | ||
properties: | ||
slack: | ||
type: string | ||
email: | ||
type: string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
apiVersion: ddukbg/v1alpha1 | ||
kind: DeploymentTracker | ||
metadata: | ||
name: my-app-deployment-tracker | ||
spec: | ||
deploymentName: my-app | ||
namespace: default | ||
notify: | ||
slack: "https://hooks.slack.com/services/..." | ||
email: "[email protected]" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"bytes" | ||
"encoding/json" | ||
|
||
appsv1 "k8s.io/api/apps/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
|
||
v1alpha1 "k8s-deploy-watcher/api/v1alpha1" | ||
) | ||
|
||
type DeploymentTrackerReconciler struct { | ||
client.Client | ||
Scheme *runtime.Scheme | ||
} | ||
|
||
func (r *DeploymentTrackerReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { | ||
logger := log.FromContext(ctx) | ||
|
||
// 1. Custom Resource 가져오기 | ||
tracker := &v1alpha1.DeploymentTracker{} | ||
err := r.Get(ctx, req.NamespacedName, tracker) | ||
if err != nil { | ||
logger.Error(err, "Failed to fetch DeploymentTracker CR") | ||
return reconcile.Result{}, client.IgnoreNotFound(err) | ||
} | ||
|
||
// 2. 해당 Deployment의 상태 확인 | ||
deployment := &appsv1.Deployment{} | ||
err = r.Get(ctx, client.ObjectKey{Name: tracker.Spec.DeploymentName, Namespace: tracker.Spec.Namespace}, deployment) | ||
if err != nil { | ||
logger.Error(err, "Failed to fetch Deployment") | ||
return reconcile.Result{}, err | ||
} | ||
|
||
// 3. 배포가 성공적으로 완료되었는지 확인 | ||
if deployment.Status.ReadyReplicas == *deployment.Spec.Replicas { | ||
logger.Info("Deployment is running successfully", "Deployment", deployment.Name) | ||
|
||
// 4. 알림 전송 | ||
if tracker.Spec.Notify.Slack != "" { | ||
err := sendSlackNotification(tracker.Spec.Notify.Slack, fmt.Sprintf("Deployment %s is successfully running", deployment.Name)) | ||
if err != nil { | ||
logger.Error(err, "Failed to send Slack notification") | ||
} | ||
} | ||
} | ||
|
||
return reconcile.Result{}, nil | ||
} | ||
|
||
func sendSlackNotification(webhookURL, message string) error { | ||
payload := map[string]string{"text": message} | ||
payloadBytes, err := json.Marshal(payload) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = http.Post(webhookURL, "application/json", bytes.NewBuffer(payloadBytes)) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
appsv1 "k8s.io/api/apps/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
"sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestReconcile_Success(t *testing.T) { | ||
// 1. 가짜 클라이언트 생성 (fake.Client) | ||
scheme := runtime.NewScheme() | ||
_ = appsv1.AddToScheme(scheme) // 필요한 스키마 추가 | ||
|
||
// 테스트용 Deployment 생성 | ||
deployment := &appsv1.Deployment{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "my-app", | ||
Namespace: "default", | ||
}, | ||
Spec: appsv1.DeploymentSpec{ | ||
Replicas: int32Ptr(2), // 2개의 파드 | ||
}, | ||
Status: appsv1.DeploymentStatus{ | ||
ReadyReplicas: 2, | ||
}, | ||
} | ||
|
||
// 가짜 클라이언트에 Deployment 추가 | ||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(deployment).Build() | ||
|
||
// 2. Reconciler 생성 | ||
r := &DeploymentTrackerReconciler{ | ||
Client: client, | ||
Scheme: scheme, | ||
} | ||
|
||
// 3. Reconcile 호출 | ||
result, err := r.Reconcile(context.TODO(), reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "my-app", | ||
Namespace: "default", | ||
}, | ||
}) | ||
|
||
// 4. 테스트 결과 확인 | ||
assert.NoError(t, err) | ||
assert.NotNil(t, result) | ||
assert.False(t, result.Requeue) // Requeue가 False여야 함 (정상 배포) | ||
} | ||
|
||
// 헬퍼 함수 | ||
func int32Ptr(i int32) *int32 { return &i } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"sigs.k8s.io/controller-runtime/pkg/manager" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"k8s-deploy-watcher/controllers" | ||
"k8s-deploy-watcher/api/v1alpha1" | ||
) | ||
|
||
func main() { | ||
// Manager 설정 | ||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ | ||
Scheme: v1alpha1.SchemeBuilder.AddToScheme, | ||
}) | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
|
||
// Reconciler 등록 | ||
if err := (&controllers.DeploymentTrackerReconciler{ | ||
Client: mgr.GetClient(), | ||
Scheme: mgr.GetScheme(), | ||
}).SetupWithManager(mgr); err != nil { | ||
os.Exit(1) | ||
} | ||
|
||
// Operator 실행 | ||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { | ||
os.Exit(1) | ||
} | ||
} |