Skip to content

Instantly share code, notes, and snippets.

@regmicmahesh
Created July 17, 2024 04:29
Show Gist options
  • Save regmicmahesh/5d7a05490dd6ac1b65315b2fb2831765 to your computer and use it in GitHub Desktop.
Save regmicmahesh/5d7a05490dd6ac1b65315b2fb2831765 to your computer and use it in GitHub Desktop.
package deploymentstrategy
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/google/uuid"
"github.com/x/x-api-v2/entity"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
applyappsv1 "k8s.io/client-go/applyconfigurations/apps/v1"
applyconfigcorev1 "k8s.io/client-go/applyconfigurations/core/v1"
applyconfigmetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
type KubernetesStrategy struct {
*kubernetes.Clientset
Namespace string
ContainerImageName string
ContainerPort int
}
func NewKubernetesStrategyOrDie(inCluster bool, namespace string, containerImageName string, containerPort int) *KubernetesStrategy {
var config *rest.Config
var err error
if inCluster {
config, err = rest.InClusterConfig()
} else {
home, _ := os.UserHomeDir()
kubeConfigPath := filepath.Join(home, ".kube/config")
config, err = clientcmd.BuildConfigFromFlags("", kubeConfigPath)
}
if err != nil {
panic(err)
}
return &KubernetesStrategy{
Clientset: kubernetes.NewForConfigOrDie(config),
Namespace: namespace,
ContainerImageName: containerImageName,
ContainerPort: containerPort,
}
}
func (k *KubernetesStrategy) Apply(ctx context.Context, bot *entity.Bot) error {
cmv1 := k.CoreV1().ConfigMaps(k.Namespace)
svcv1 := k.CoreV1().Services(k.Namespace)
pvcv1 := k.CoreV1().PersistentVolumeClaims(k.Namespace)
deployv1 := k.AppsV1().Deployments(k.Namespace)
// create bot configmap
botcm := applyconfigcorev1.ConfigMap(bot.ID, k.Namespace).WithData(
map[string]string{
"APP_BOT_ID": bot.ID,
"APP_BOT_EXCHANGE_NAME": bot.Config.EXCHANGE,
"APP_BOT_TOKEN_PAIR": bot.Config.PAIR,
},
)
_, err := cmv1.Apply(context.Background(), botcm, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create redis configmap
rediscm := applyconfigcorev1.ConfigMap(fmt.Sprintf("%s-redis", bot.ID), k.Namespace).WithData(
map[string]string{
"redis-config": `appendonly yes
appendfsync everysec
`,
},
)
_, err = cmv1.Apply(context.Background(), rediscm, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create redis persistent volume claims.
redispvc := applyconfigcorev1.PersistentVolumeClaim(fmt.Sprintf("%s-redis", bot.ID), k.Namespace).WithSpec(
applyconfigcorev1.PersistentVolumeClaimSpec().WithAccessModes("ReadWriteOnce").
WithResources(applyconfigcorev1.VolumeResourceRequirements().WithRequests(v1.ResourceList{
v1.ResourceStorage: resource.MustParse("3Gi"),
})).WithStorageClassName("do-block-storage"),
)
_, err = pvcv1.Apply(context.Background(), redispvc, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create redis deployment.
redisdeploy := applyappsv1.Deployment(fmt.Sprintf("%s-redis", bot.ID), k.Namespace).WithSpec(
applyappsv1.DeploymentSpec().
WithReplicas(1).
WithSelector(
applyconfigmetav1.LabelSelector().WithMatchLabels(map[string]string{
"app": "redis",
"bot": bot.ID,
}),
).
WithTemplate(
applyconfigcorev1.PodTemplateSpec().WithLabels(map[string]string{"app": "redis", "bot": bot.ID}).WithSpec(
applyconfigcorev1.PodSpec().WithContainers(
applyconfigcorev1.Container().WithName("redis").WithImage("redis:latest").WithCommand("redis-server", "/user-conf/redis.conf").WithPorts(applyconfigcorev1.ContainerPort().WithContainerPort(6379)).WithVolumeMounts(
applyconfigcorev1.VolumeMount().WithMountPath("/data").WithName("redis-data"),
applyconfigcorev1.VolumeMount().WithMountPath("/user-conf").WithName("config"),
),
).WithVolumes(
applyconfigcorev1.Volume().WithName("redis-data").WithPersistentVolumeClaim(applyconfigcorev1.PersistentVolumeClaimVolumeSource().WithClaimName(fmt.Sprintf("%s-redis", bot.ID))),
applyconfigcorev1.Volume().WithName("config").WithConfigMap(applyconfigcorev1.ConfigMapVolumeSource().WithName(fmt.Sprintf("%s-redis", bot.ID)).WithItems(applyconfigcorev1.KeyToPath().WithKey("redis-config").WithPath("redis.conf")))),
),
),
)
_, err = deployv1.Apply(context.Background(), redisdeploy, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create redis service.
redissvc := applyconfigcorev1.Service(fmt.Sprintf("%s-redis", bot.ID), k.Namespace).WithSpec(
applyconfigcorev1.ServiceSpec().WithSelector(
map[string]string{
"app": "redis",
"bot": bot.ID,
},
).WithPorts(applyconfigcorev1.ServicePort().WithPort(6379)),
)
_, err = svcv1.Apply(context.Background(), redissvc, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create bot deployment.
botdeploy := applyappsv1.Deployment(fmt.Sprintf("%s-bot", bot.ID), k.Namespace).WithSpec(
applyappsv1.DeploymentSpec().
WithReplicas(1).
WithSelector(
applyconfigmetav1.LabelSelector().WithMatchLabels(map[string]string{
"app": "bot",
"bot": bot.ID,
}),
).
WithTemplate(
applyconfigcorev1.PodTemplateSpec().WithLabels(map[string]string{"app": "bot", "bot": bot.ID}).WithSpec(
applyconfigcorev1.PodSpec().WithContainers(
applyconfigcorev1.Container().WithName("bot").WithImage(k.ContainerImageName).WithEnvFrom(
applyconfigcorev1.EnvFromSource().WithConfigMapRef(applyconfigcorev1.ConfigMapEnvSource().WithName(bot.ID)),
)),
),
),
)
_, err = deployv1.Apply(context.Background(), botdeploy, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
// create bot service.
botsvc := applyconfigcorev1.Service(fmt.Sprintf("%s-bot", bot.ID), k.Namespace).WithSpec(
applyconfigcorev1.ServiceSpec().WithSelector(
map[string]string{
"app": "bot",
"bot": bot.ID,
},
).WithPorts(applyconfigcorev1.ServicePort().WithPort(int32(k.ContainerPort))),
)
_, err = svcv1.Apply(context.Background(), botsvc, metav1.ApplyOptions{
FieldManager: "admin-panel",
})
if err != nil {
return err
}
return nil
}
func (k *KubernetesStrategy) Terminate(ctx context.Context, botId string) error {
cmv1 := k.CoreV1().ConfigMaps(k.Namespace)
svcv1 := k.CoreV1().Services(k.Namespace)
pvcv1 := k.CoreV1().PersistentVolumeClaims(k.Namespace)
deployv1 := k.AppsV1().Deployments(k.Namespace)
// delete redis components
err := deployv1.Delete(context.Background(), fmt.Sprintf("%s-redis", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
err = cmv1.Delete(context.Background(), fmt.Sprintf("%s-redis", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
err = pvcv1.Delete(context.Background(), fmt.Sprintf("%s-redis", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
err = svcv1.Delete(context.Background(), fmt.Sprintf("%s-redis", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
// delete bot components
err = cmv1.Delete(context.Background(), botId, metav1.DeleteOptions{})
if err != nil {
return err
}
err = svcv1.Delete(context.Background(), fmt.Sprintf("%s-bot", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
err = deployv1.Delete(context.Background(), fmt.Sprintf("%s-bot", botId), metav1.DeleteOptions{})
if err != nil {
return err
}
return nil
}
func (k *KubernetesStrategy) FetchRunningIDs(ctx context.Context) ([]string, error) {
c := k.CoreV1().ConfigMaps(k.Namespace)
cms, err := c.List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
ids := []string{}
for _, v := range cms.Items {
cmName := v.GetName()
// the format of bot container name is "bot-{uuid:v4}"
probableUuid := strings.TrimPrefix(cmName, "bot-")
if _, err := uuid.Parse(probableUuid); err == nil {
ids = append(ids, cmName)
}
}
return ids, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment