Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mrMigles authored and seiv0814 committed Nov 14, 2024
1 parent 56e62dc commit 3b17876
Show file tree
Hide file tree
Showing 33 changed files with 3,098 additions and 1 deletion.
58 changes: 58 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Build Artifacts
on:
push:
branches:
- '**'

jobs:
multiplatform_build:
strategy:
fail-fast: false
matrix:
component:
- name: disaster-recovery-daemon
file: docker/Dockerfile
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v5
with:
no-cache: true
context: ${{ matrix.component.dir }}
file: ${{ matrix.component.file }}
platforms: linux/amd64,linux/arm64
push: false
tags: ${{ matrix.component.name }}
provenance: false
# Step 2: Set up Go environment
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21' # specify the version you need

# Step 3: Cache Go modules to speed up build times
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# Step 4: Install dependencies
- name: Install dependencies
run: go mod download

# Step 5: Run unit tests
- name: Run tests
run: go test -v ./...

# Step 6: Build the package
- name: Build
run: go build -v ./...
79 changes: 79 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
.idea/
# Temporary Build Files
build/_output
build/_test
# Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
dist/
# Flycheck
flycheck_*.el
# server auth directory
/server/
# projectiles files
.projectile
projectile-bookmarks.eld
# directory configuration
.dir-locals.el
# saveplace
places
# url cache
url/cache/
# cedet
ede-projects.el
# smex
smex-items
# company-statistics
company-statistics-cache.el
# anaconda-mode
anaconda-mode/
### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with 'go test -c'
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### Vim ###
# swap
.sw[a-p]
.*.sw[a-p]
# session
Session.vim
# temporary
.netrwhist
# auto-generated tag files
tags
### VisualStudioCode ###
.vscode/*
.history
# End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode
*.iml
392 changes: 391 additions & 1 deletion README.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions _demo/config-map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: ConfigMap
apiVersion: v1
metadata:
name: consul-dr-cm
namespace: consul-service
annotations:
switchoverRetry: ""
data:
mode: active
noWait: 'false'
status_comment: ""
status_mode: active
status_status: done
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="go build git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/_demo" type="GoApplicationRunConfiguration" factoryName="Go Application" nameIsGenerated="true">
<module name="disaster-recovery-daemon" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="--kubeconfig $PROJECT_DIR$/_demo/kubeconfig" />
<envs>
<env name="DISASTER_RECOVERY_MODE_PATH" value="data.mode" />
<env name="DISASTER_RECOVERY_NOWAIT_AS_STRING" value="true" />
<env name="DISASTER_RECOVERY_NOWAIT_PATH" value="data.noWait" />
<env name="DISASTER_RECOVERY_STATUS_COMMENT_PATH" value="data.status_comment" />
<env name="DISASTER_RECOVERY_STATUS_MODE_PATH" value="data.status_mode" />
<env name="DISASTER_RECOVERY_STATUS_STATUS_PATH" value="data.status_status" />
<env name="HEALTH_MAIN_SERVICES_ACTIVE" value="statefulset consul-server" />
<env name="IN_CLUSTER_CONFIG" value="false" />
<env name="NAMESPACE" value="consul-service" />
<env name="RESOURCE_FOR_DR" value="&quot;&quot; v1 configmaps consul-dr-cm" />
<env name="TREAT_STATUS_AS_FIELD" value="true" />
<env name="USE_DEFAULT_PATHS" value="false" />
</envs>
<kind value="PACKAGE" />
<package value="git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/_demo" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>
Empty file added _demo/kubeconfig
Empty file.
64 changes: 64 additions & 0 deletions _demo/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"context"
"git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/api/entity"
"git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/client"
"git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/config"
"git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/controller"
"git.netcracker.com/PROD.Platform.Streaming/disaster-recovery-daemon/server"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"log"
"time"
)

func main() {
// Make a config loader
cfgLoader := config.GetDefaultEnvConfigLoader()

// Build a config
cfg, err := config.NewConfig(cfgLoader)
if err != nil {
log.Fatalln(err.Error())
}

// Easy way to create a kubernetes client if necessary
kubeClient := client.MakeKubeClientSet()

// Start DRD server with custom health function inside, which calculates on;y additional health status (fullHealth: false)
go server.NewServer(cfg).
WithHealthFunc(func(request entity.HealthRequest) (entity.HealthResponse, error) {
_, err := kubeClient.CoreV1().Pods("consul-service").Get(context.TODO(), "consul-server-0", metav1.GetOptions{})
if err != nil {
log.Printf("Error: %s", err)
}
return entity.HealthResponse{Status: entity.DOWN}, nil
}, false).
Run()

// Start DRD controller with external function
controller.NewController(cfg).
WithFunc(drFunction).
WithRetry(3, time.Second*1).
Run()
}

// DR function
func drFunction(controllerRequest entity.ControllerRequest) (entity.ControllerResponse, error) {
var configMap v1.ConfigMap
err := runtime.DefaultUnstructuredConverter.FromUnstructured(controllerRequest.Object, &configMap)
if err != nil {
log.Printf("Error: %s", err)
return entity.ControllerResponse{}, err
}

return entity.ControllerResponse{
SwitchoverState: entity.SwitchoverState{
Mode: controllerRequest.Mode,
Status: "done",
Comment: "done",
},
}, nil
}
62 changes: 62 additions & 0 deletions api/entity/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package entity

import "k8s.io/apimachinery/pkg/watch"

const (
ACTIVE = "active"
DISABLED = "disable"
STANDBY = "standby"
DEGRADED = "degraded"
QUEUE = "queue"
RUNNING = "running"
DONE = "done"
DOWN = "down"
UP = "up"
FAILED = "failed"
DeploymentType = "deployment"
StatefulsetType = "statefulset"
)

type SwitchoverState struct {
Mode string `json:"mode"`
Status string `json:"status,omitempty"`
Comment string `json:"comment,omitempty"`
}

type RequestData struct {
Mode string `json:"mode"`
NoWait *bool `json:"no-wait,omitempty"`
}

type ControllerRequest struct {
RequestData
SwitchoverAnnotation string `json:"switchoverAnnotation,omitempty"`
Status SwitchoverState `json:"status"`
EventType watch.EventType `json:"eventType"`
Object map[string]interface{} `json:"object"`
}

type ControllerResponse struct {
SwitchoverState
}

type HealthRequest struct {
Mode string
FullHealth bool
}

type HealthResponse struct {
Status string `json:"status"`
Comment string `json:"comment,omitempty"`
}

type ModeDataUpdate struct {
Mode string
NoWait bool
Annotation map[string]string
}

type StatusResponse struct {
Status string `json:"status"`
Message string `json:"message,omitempty"`
}
16 changes: 16 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

DOCKER_FILE=docker/Dockerfile
TARGET_DIR=target

mkdir -p ${TARGET_DIR}

echo "Build docker image"
for docker_image_name in ${DOCKER_NAMES}; do
echo "Docker image name: $docker_image_name"
docker build \
--file=${DOCKER_FILE} \
--pull \
-t "${docker_image_name}" \
.
done
69 changes: 69 additions & 0 deletions client/kubernetes_clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package client

import (
"flag"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"log"
"os"
"path/filepath"
)

var kubeconfig = new(string)

func getKubeConfig() *string {
if *kubeconfig != "" {
return kubeconfig
}
if home := homeDir(); home != "" {
kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()
return kubeconfig
}

func getConfigurationForKubernetesClient() *rest.Config {
var config *rest.Config
var err error
inClusterConfig := os.Getenv("IN_CLUSTER_CONFIG")
if inClusterConfig == "" || inClusterConfig == "true" {
config, err = rest.InClusterConfig()
} else {
kubeconfig := getKubeConfig()
config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
}
if err != nil {
log.Fatalln(err, "Can not get kubernetes config")
return nil
}
return config
}

func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}

func MakeDynamicClient() dynamic.Interface {
config := getConfigurationForKubernetesClient()
client, err := dynamic.NewForConfig(config)
if err != nil {
log.Fatalln(err, "Can not get dynamic kubernetes client")
}
return client
}

func MakeKubeClientSet() *kubernetes.Clientset {
config := getConfigurationForKubernetesClient()
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
log.Fatalln(err, "Can not get kubernetes client")
}
return clientset
}
Loading

0 comments on commit 3b17876

Please sign in to comment.