You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by ka...@apache.org on 2021/04/22 05:48:30 UTC

[submarine] branch master updated: SUBMARINE-799. Create a submarine-server for a single user

This is an automated email from the ASF dual-hosted git repository.

kaihsun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new 860c043  SUBMARINE-799. Create a submarine-server for a single user
860c043 is described below

commit 860c04301a806466d261ecee7d3e8cb4a58bec0a
Author: kevin85421 <b0...@ntu.edu.tw>
AuthorDate: Thu Apr 22 13:48:21 2021 +0800

    SUBMARINE-799. Create a submarine-server for a single user
    
    ### What is this PR for?
    Create a submarine server for a single user via the submarine operator.
    
    Reference: https://github.com/apache/submarine/blob/master/helm-charts/submarine/templates/submarine-server.yaml
    
    ### What type of PR is it?
    [Feature]
    
    ### Todos
    
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/SUBMARINE-799
    
    ### How should this be tested?
    https://user-images.githubusercontent.com/20109646/115583344-62eba200-a2fc-11eb-90d2-72acc9d13938.mov
    
    
    
    ### Screenshots (if appropriate)
    * service/submarine-server (submarine-operator)
    <img width="414" alt="截圖 2021-04-21 下午11 41 37" src="https://user-images.githubusercontent.com/20109646/115583500-86aee800-a2fc-11eb-8ee2-04391cdf5ce0.png">
    
    * service/submarine-server (helm)
    <img width="794" alt="截圖 2021-04-21 下午11 47 56" src="https://user-images.githubusercontent.com/20109646/115583717-bb22a400-a2fc-11eb-99e9-c00ba505dbe6.png">
    
    * serviceaccount/submarine-server (submarine-operator)
    <img width="447" alt="截圖 2021-04-21 下午11 41 42" src="https://user-images.githubusercontent.com/20109646/115583564-96c6c780-a2fc-11eb-9343-60fbb5cee049.png">
    
    * serviceaccount/submarine-server (helm)
    <img width="804" alt="截圖 2021-04-21 下午11 48 19" src="https://user-images.githubusercontent.com/20109646/115583796-cd9cdd80-a2fc-11eb-8a1b-d7ec76c6c41c.png">
    
    * deployment/submarine-server (submarine-operator)
    <img width="824" alt="截圖 2021-04-21 下午11 41 51" src="https://user-images.githubusercontent.com/20109646/115583632-a6461080-a2fc-11eb-9875-d187499de27c.png">
    
    * deployment/submarine-server (helm)
    <img width="886" alt="截圖 2021-04-21 下午11 48 49" src="https://user-images.githubusercontent.com/20109646/115583870-dd1c2680-a2fc-11eb-8404-f849afad9f2a.png">
    
    
    
    ### Questions:
    * Do the license files need updating? No
    * Are there breaking changes for older versions? No
    * Does this need new documentation? No
---
 submarine-cloud-v2/controller.go | 220 ++++++++++++++++++++++++++++++++-------
 submarine-cloud-v2/main.go       |  16 +--
 2 files changed, 189 insertions(+), 47 deletions(-)

diff --git a/submarine-cloud-v2/controller.go b/submarine-cloud-v2/controller.go
index 30d686a..3ddd821 100644
--- a/submarine-cloud-v2/controller.go
+++ b/submarine-cloud-v2/controller.go
@@ -18,26 +18,33 @@
 package main
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	clientset "submarine-cloud-v2/pkg/generated/clientset/versioned"
+	submarinescheme "submarine-cloud-v2/pkg/generated/clientset/versioned/scheme"
+	informers "submarine-cloud-v2/pkg/generated/informers/externalversions/submarine/v1alpha1"
+	listers "submarine-cloud-v2/pkg/generated/listers/submarine/v1alpha1"
+	"time"
+
+	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/util/intstr"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/apimachinery/pkg/util/wait"
 	appsinformers "k8s.io/client-go/informers/apps/v1"
+	coreinformers "k8s.io/client-go/informers/core/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/kubernetes/scheme"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+	appslisters "k8s.io/client-go/listers/apps/v1"
+	corelisters "k8s.io/client-go/listers/core/v1"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/tools/record"
 	"k8s.io/client-go/util/workqueue"
 	"k8s.io/klog/v2"
-	clientset "submarine-cloud-v2/pkg/generated/clientset/versioned"
-	submarinescheme "submarine-cloud-v2/pkg/generated/clientset/versioned/scheme"
-	"submarine-cloud-v2/pkg/helm"
-	informers "submarine-cloud-v2/pkg/generated/informers/externalversions/submarine/v1alpha1"
-	listers "submarine-cloud-v2/pkg/generated/listers/submarine/v1alpha1"
-	"time"
 )
 
 const controllerAgentName = "submarine-controller"
@@ -52,6 +59,9 @@ type Controller struct {
 	submarinesLister listers.SubmarineLister
 	submarinesSynced cache.InformerSynced
 
+	deploymentLister     appslisters.DeploymentLister
+	serviceaccountLister corelisters.ServiceAccountLister
+	serviceLister        corelisters.ServiceLister
 	// workqueue is a rate limited work queue. This is used to queue work to be
 	// processed instead of performing it as soon as a change happens. This
 	// means we can ensure we only process a fixed amount of resources at a
@@ -68,6 +78,8 @@ func NewController(
 	kubeclientset kubernetes.Interface,
 	submarineclientset clientset.Interface,
 	deploymentInformer appsinformers.DeploymentInformer,
+	serviceInformer coreinformers.ServiceInformer,
+	serviceaccountInformer coreinformers.ServiceAccountInformer,
 	submarineInformer informers.SubmarineInformer) *Controller {
 
 	// TODO: Create event broadcaster
@@ -82,12 +94,15 @@ func NewController(
 
 	// Initialize controller
 	controller := &Controller{
-		kubeclientset:      kubeclientset,
-		submarineclientset: submarineclientset,
-		submarinesLister:   submarineInformer.Lister(),
-		submarinesSynced:   submarineInformer.Informer().HasSynced,
-		workqueue:          workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Submarines"),
-		recorder:           recorder,
+		kubeclientset:        kubeclientset,
+		submarineclientset:   submarineclientset,
+		submarinesLister:     submarineInformer.Lister(),
+		submarinesSynced:     submarineInformer.Informer().HasSynced,
+		deploymentLister:     deploymentInformer.Lister(),
+		serviceLister:        serviceInformer.Lister(),
+		serviceaccountLister: serviceaccountInformer.Lister(),
+		workqueue:            workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Submarines"),
+		recorder:             recorder,
 	}
 
 	// Setting up event handler for Submarine
@@ -117,34 +132,6 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
 		return fmt.Errorf("failed to wait for caches to sync")
 	}
 
-	// Example: HelmInstall (can be removed in the future):
-	// This is equal to:
-	// 		helm repo add k8s-as-helm https://ameijer.github.io/k8s-as-helm/
-	// .	helm repo update
-	//  	helm install helm-install-example-release k8s-as-helm/svc --set ports[0].protocol=TCP,ports[0].port=80,ports[0].targetPort=9376
-	// Useful Links:
-	//   (1) https://github.com/PrasadG193/helm-clientgo-example
-	// . (2) https://github.com/ameijer/k8s-as-helm/tree/master/charts/svc
-	klog.Info("[Helm example] Install")
-	helmActionConfig := helm.HelmInstall(
-		"https://ameijer.github.io/k8s-as-helm/",
-		"k8s-as-helm",
-		"svc",
-		"helm-install-example-release",
-		"default",
-		map[string]string {
-			"set": "ports[0].protocol=TCP,ports[0].port=80,ports[0].targetPort=9376",
-		},
-	)
-
-	klog.Info("[Helm example] Sleep 60 seconds")
-	time.Sleep(time.Duration(60) * time.Second)
-
-	klog.Info("[Helm example] Uninstall")
-	helm.HelmUninstall("helm-install-example-release", helmActionConfig)
-
-
-
 	klog.Info("Starting workers")
 	// Launch two workers to process Submarine resources
 	for i := 0; i < threadiness; i++ {
@@ -193,6 +180,147 @@ func (c *Controller) processNextWorkItem() bool {
 	return true
 }
 
+// newSubmarineServer is a function to create submarine-server.
+// Reference: https://github.com/apache/submarine/blob/master/helm-charts/submarine/templates/submarine-server.yaml
+func (c *Controller) newSubmarineServer(serverImage string, serverReplicas int32, namespace string) error {
+	klog.Info("[newSubmarineServer]")
+	serverName := "submarine-server"
+
+	// Step1: Create ServiceAccount
+	serviceaccount, serviceaccount_err := c.serviceaccountLister.ServiceAccounts(namespace).Get(serverName)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(serviceaccount_err) {
+		serviceaccount, serviceaccount_err = c.kubeclientset.CoreV1().ServiceAccounts(namespace).Create(context.TODO(),
+			&corev1.ServiceAccount{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: serverName,
+				},
+			},
+			metav1.CreateOptions{})
+		klog.Info("	Create ServiceAccount: ", serviceaccount.Name)
+	}
+
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if serviceaccount_err != nil {
+		return serviceaccount_err
+	}
+
+	// TODO: (sample-controller) controller.go:287 ~ 293
+
+	// Step2: Create Service
+	service, service_err := c.serviceLister.Services(namespace).Get(serverName)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(service_err) {
+		service, service_err = c.kubeclientset.CoreV1().Services(namespace).Create(context.TODO(),
+			&corev1.Service{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: serverName,
+					Labels: map[string]string{
+						"run": serverName,
+					},
+				},
+				Spec: corev1.ServiceSpec{
+					Ports: []corev1.ServicePort{
+						{
+							Port:       8080,
+							TargetPort: intstr.FromInt(8080),
+							Protocol:   "TCP",
+						},
+					},
+					Selector: map[string]string{
+						"run": serverName,
+					},
+				},
+			},
+			metav1.CreateOptions{})
+		klog.Info("	Create Service: ", service.Name)
+	}
+
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if service_err != nil {
+		return service_err
+	}
+
+	// TODO: (sample-controller) controller.go:287 ~ 293
+
+	// Step3: Create Deployment
+	deployment, deployment_err := c.deploymentLister.Deployments(namespace).Get(serverName)
+	// If the resource doesn't exist, we'll create it
+	if errors.IsNotFound(deployment_err) {
+		deployment, deployment_err = c.kubeclientset.AppsV1().Deployments(namespace).Create(context.TODO(),
+			&appsv1.Deployment{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: serverName,
+				},
+				Spec: appsv1.DeploymentSpec{
+					Selector: &metav1.LabelSelector{
+						MatchLabels: map[string]string{
+							"run": serverName,
+						},
+					},
+					Replicas: &serverReplicas,
+					Template: corev1.PodTemplateSpec{
+						ObjectMeta: metav1.ObjectMeta{
+							Labels: map[string]string{
+								"run": serverName,
+							},
+						},
+						Spec: corev1.PodSpec{
+							ServiceAccountName: serverName,
+							Containers: []corev1.Container{
+								{
+									Name:  serverName,
+									Image: serverImage,
+									Env: []corev1.EnvVar{
+										{
+											Name:  "SUBMARINE_SERVER_PORT",
+											Value: "8080",
+										},
+										{
+											Name:  "SUBMARINE_SERVER_PORT_8080_TCP",
+											Value: "8080",
+										},
+										{
+											Name:  "SUBMARINE_SERVER_DNS_NAME",
+											Value: serverName + "." + namespace,
+										},
+										{
+											Name:  "K8S_APISERVER_URL",
+											Value: "kubernetes.default.svc",
+										},
+									},
+									Ports: []corev1.ContainerPort{
+										{
+											ContainerPort: 8080,
+										},
+									},
+									ImagePullPolicy: "IfNotPresent",
+								},
+							},
+						},
+					},
+				},
+			},
+			metav1.CreateOptions{})
+		klog.Info("	Create Deployment: ", deployment.Name)
+	}
+
+	// If an error occurs during Get/Create, we'll requeue the item so we can
+	// attempt processing again later. This could have been caused by a
+	// temporary network failure, or any other transient reason.
+	if deployment_err != nil {
+		return deployment_err
+	}
+
+	// TODO: (sample-controller) controller.go:287 ~ 293
+
+	return nil
+}
+
 // syncHandler compares the actual state with the desired, and attempts to
 // converge the two. It then updates the Status block of the Foo resource
 // with the current status of the resource.
@@ -223,6 +351,18 @@ func (c *Controller) syncHandler(key string) error {
 	b, err := json.MarshalIndent(submarine.Spec, "", "  ")
 	fmt.Println(string(b))
 
+	// Create submarine-server
+	serverImage := submarine.Spec.Server.Image
+	serverReplicas := *submarine.Spec.Server.Replicas
+	if serverImage == "" {
+		serverImage = "apache/submarine:server-" + submarine.Spec.Version
+	}
+	err = c.newSubmarineServer(serverImage, serverReplicas, namespace)
+
+	if err != nil {
+		return err
+	}
+
 	return nil
 }
 
diff --git a/submarine-cloud-v2/main.go b/submarine-cloud-v2/main.go
index 1d94803..8356dee 100644
--- a/submarine-cloud-v2/main.go
+++ b/submarine-cloud-v2/main.go
@@ -19,16 +19,16 @@ package main
 
 import (
 	"flag"
-	"time"
 	kubeinformers "k8s.io/client-go/informers"
-	"k8s.io/klog/v2"
-	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"k8s.io/klog/v2"
 	"os"
-	"submarine-cloud-v2/pkg/signals"
 	clientset "submarine-cloud-v2/pkg/generated/clientset/versioned"
 	informers "submarine-cloud-v2/pkg/generated/informers/externalversions"
+	"submarine-cloud-v2/pkg/signals"
+	"time"
 )
 
 var (
@@ -52,7 +52,7 @@ func main() {
 	stopCh := signals.SetupSignalHandler()
 
 	cfg, err := initKubeConfig()
-	
+
 	if err != nil {
 		klog.Fatalf("Error building kubeconfig: %s", err.Error())
 	}
@@ -69,13 +69,15 @@ func main() {
 
 	kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)
 	submarineInformerFactory := informers.NewSharedInformerFactory(submarineClient, time.Second*30)
-	
+
 	// TODO: Pass informers to NewController()
 	//       ex: namespace informer
 
 	// Create a Submarine operator
 	controller := NewController(kubeClient, submarineClient,
 		kubeInformerFactory.Apps().V1().Deployments(),
+		kubeInformerFactory.Core().V1().Services(),
+		kubeInformerFactory.Core().V1().ServiceAccounts(),
 		submarineInformerFactory.Submarine().V1alpha1().Submarines())
 
 	// notice that there is no need to run Start methods in a separate goroutine. (i.e. go kubeInformerFactory.Start(stopCh)
@@ -91,6 +93,6 @@ func main() {
 
 func init() {
 	flag.BoolVar(&incluster, "incluster", false, "Run submarine-operator in-cluster")
-	flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("HOME") + "/.kube/config", "Path to a kubeconfig. Only required if out-of-cluster.")
+	flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("HOME")+"/.kube/config", "Path to a kubeconfig. Only required if out-of-cluster.")
 	flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org