-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: automatically deploy the deployment manifests and service when …
…TelemetryService presents. (#1043) * feat: create service and deployment when detect telemetryservice * test: add telemetryservice controller test * chore: rename function name and some small changes * fix: log msg format * fix: one type in deployment creation
- Loading branch information
1 parent
4c322ee
commit dad0e0c
Showing
2 changed files
with
277 additions
and
5 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 |
---|---|---|
|
@@ -18,8 +18,15 @@ package controllers | |
|
||
import ( | ||
"context" | ||
|
||
"github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" | ||
appsv1 "k8s.io/api/apps/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/intstr" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
|
@@ -31,9 +38,19 @@ type TelemetryServiceReconciler struct { | |
Scheme *runtime.Scheme | ||
} | ||
|
||
var tsNamespacedName = types.NamespacedName{ | ||
Namespace: "shifu-service", | ||
Name: "telemetryservice", | ||
} | ||
|
||
// image of telemetryservice deployment | ||
const IMAGE = "edgehub/telemetryservice:nightly" | ||
|
||
//+kubebuilder:rbac:groups=shifu.edgenesis.io,resources=telemetryservices,verbs=get;list;watch;create;update;patch;delete | ||
//+kubebuilder:rbac:groups=shifu.edgenesis.io,resources=telemetryservices/status,verbs=get;update;patch | ||
//+kubebuilder:rbac:groups=shifu.edgenesis.io,resources=telemetryservices/finalizers,verbs=update | ||
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete | ||
//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete | ||
|
||
// Reconcile is part of the main kubernetes reconciliation loop which aims to | ||
// move the current state of the cluster closer to the desired state. | ||
|
@@ -46,7 +63,6 @@ type TelemetryServiceReconciler struct { | |
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile | ||
func (r *TelemetryServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
rlog := log.FromContext(ctx) | ||
|
||
ts := &v1alpha1.TelemetryService{} | ||
if err := r.Get(ctx, req.NamespacedName, ts); err != nil { | ||
rlog.Error(err, "Unable to fetch TelemetryService") | ||
|
@@ -55,10 +71,23 @@ func (r *TelemetryServiceReconciler) Reconcile(ctx context.Context, req ctrl.Req | |
// on deleted requests. | ||
return ctrl.Result{}, client.IgnoreNotFound(err) | ||
} | ||
|
||
// TODO: reconcile TelemetryService services | ||
rlog.Info("Hello World! This is telemetryservice_controller reconciling") | ||
|
||
deploy := &appsv1.Deployment{} | ||
if err := r.Get(ctx, tsNamespacedName, deploy); err != nil { | ||
if errors.IsNotFound(err) { | ||
if err := CreateTelemetryServiceDeployment(ctx, r, ts, req); err != nil { | ||
rlog.Error(err, "Failed to create TelemetryService deployment") | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
} | ||
service := &corev1.Service{} | ||
if err := r.Get(ctx, tsNamespacedName, service); err != nil { | ||
if err := CreateTelemetryServiceService(ctx, r, ts, req); err != nil { | ||
rlog.Error(err, "Failed to create TelemetryService service") | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
rlog.Info("Reconciling TelemetryService") | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
|
@@ -68,3 +97,78 @@ func (r *TelemetryServiceReconciler) SetupWithManager(mgr ctrl.Manager) error { | |
For(&v1alpha1.TelemetryService{}). | ||
Complete(r) | ||
} | ||
|
||
func CreateTelemetryServiceService(ctx context.Context, r *TelemetryServiceReconciler, ts *v1alpha1.TelemetryService, req ctrl.Request) error { | ||
rlog := log.FromContext(ctx) | ||
svc := &corev1.Service{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: tsNamespacedName.Name, | ||
Namespace: tsNamespacedName.Namespace, | ||
}, | ||
Spec: corev1.ServiceSpec{ | ||
Selector: map[string]string{"app": tsNamespacedName.Name}, | ||
Ports: []corev1.ServicePort{ | ||
{ | ||
Port: 80, | ||
Protocol: corev1.ProtocolTCP, | ||
TargetPort: intstr.IntOrString{IntVal: 8080}, | ||
}, | ||
}, | ||
Type: corev1.ServiceTypeLoadBalancer, | ||
}, | ||
} | ||
rlog.Info("Start creating TelemetryService service") | ||
if err := r.Create(ctx, svc); err != nil { | ||
rlog.Error(err, "Failed to create a new TelemetryService service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func CreateTelemetryServiceDeployment(ctx context.Context, r *TelemetryServiceReconciler, ts *v1alpha1.TelemetryService, req ctrl.Request) error { | ||
rlog := log.FromContext(ctx) | ||
deploy := &appsv1.Deployment{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: tsNamespacedName.Name, | ||
Namespace: tsNamespacedName.Namespace, | ||
}, | ||
Spec: appsv1.DeploymentSpec{ | ||
Selector: &metav1.LabelSelector{ | ||
MatchLabels: map[string]string{"app": tsNamespacedName.Name}, | ||
}, | ||
Template: corev1.PodTemplateSpec{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Labels: map[string]string{"app": tsNamespacedName.Name}, | ||
}, | ||
Spec: corev1.PodSpec{ | ||
Containers: []corev1.Container{ | ||
{ | ||
Name: tsNamespacedName.Name, | ||
Image: IMAGE, | ||
Ports: []corev1.ContainerPort{ | ||
{ContainerPort: 8080}, | ||
}, | ||
Env: []corev1.EnvVar{ | ||
{ | ||
Name: "SERVER_LISTEN_PORT", | ||
Value: ":8080", | ||
}, | ||
{ | ||
Name: "EDGEDEVICE_NAMESPACE", | ||
Value: "devices", | ||
}, | ||
}, | ||
}, | ||
}, | ||
ServiceAccountName: "telemetry-service-sa", | ||
}, | ||
}, | ||
}, | ||
} | ||
rlog.Info("Start creating TelemetryService deployment") | ||
if err := r.Create(ctx, deploy); err != nil { | ||
rlog.Error(err, "Failed to create a new TelemetryService deployment", "Deployment.Namespace", deploy.Namespace, "Deployment.Name", deploy.Name) | ||
return err | ||
} | ||
return nil | ||
} |
168 changes: 168 additions & 0 deletions
168
pkg/k8s/controllers/telemetryservice_controller_test.go
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,168 @@ | ||
/* | ||
Copyright 2021. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package controllers | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/edgenesis/shifu/pkg/k8s/api/v1alpha1" | ||
"github.com/stretchr/testify/assert" | ||
appsv1 "k8s.io/api/apps/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/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" | ||
) | ||
|
||
func setupScheme(scheme *runtime.Scheme) { | ||
_ = v1alpha1.AddToScheme(scheme) | ||
_ = appsv1.AddToScheme(scheme) | ||
_ = corev1.AddToScheme(scheme) | ||
} | ||
|
||
func TestCreateService(t *testing.T) { | ||
// Setup the test environment | ||
scheme := runtime.NewScheme() | ||
setupScheme(scheme) | ||
// Create a fake client for the reconciler | ||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() | ||
|
||
// Create a TelemetryService instance | ||
ts := &v1alpha1.TelemetryService{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
} | ||
|
||
// Create the reconciler | ||
reconciler := &TelemetryServiceReconciler{ | ||
Client: fakeClient, | ||
Scheme: scheme, | ||
} | ||
|
||
// Call CreateTelemetryServiceService function | ||
err := CreateTelemetryServiceService(context.Background(), reconciler, ts, reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
}) | ||
|
||
// Assert that there were no errors | ||
assert.NoError(t, err) | ||
|
||
// Assert that the Service was created | ||
service := &corev1.Service{} | ||
err = fakeClient.Get(context.Background(), types.NamespacedName{Name: "telemetryservice", Namespace: "shifu-service"}, service) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, service) | ||
} | ||
|
||
func TestCreateDeployment(t *testing.T) { | ||
// Setup the test environment | ||
scheme := runtime.NewScheme() | ||
setupScheme(scheme) | ||
|
||
// Create a fake client for the reconciler | ||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() | ||
|
||
// Create a TelemetryService instance | ||
ts := &v1alpha1.TelemetryService{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
} | ||
|
||
// Create the reconciler | ||
reconciler := &TelemetryServiceReconciler{ | ||
Client: fakeClient, | ||
Scheme: scheme, | ||
} | ||
|
||
// Call CreateTelemetryServiceDeployment function | ||
err := CreateTelemetryServiceDeployment(context.Background(), reconciler, ts, reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
}) | ||
|
||
// Assert that there were no errors | ||
assert.NoError(t, err) | ||
|
||
// Assert that the Deployment was created | ||
deployment := &appsv1.Deployment{} | ||
err = fakeClient.Get(context.Background(), types.NamespacedName{Name: "telemetryservice", Namespace: "shifu-service"}, deployment) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, deployment) | ||
} | ||
|
||
func TestTelemetryServiceReconcile(t *testing.T) { | ||
// Setup the test environment | ||
scheme := runtime.NewScheme() | ||
setupScheme(scheme) | ||
|
||
// Create a fake client for the reconciler | ||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() | ||
|
||
// Create a TelemetryService instance | ||
ts := &v1alpha1.TelemetryService{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
} | ||
// Add the above objects to the fake client | ||
err := fakeClient.Create(context.Background(), ts) | ||
assert.NoError(t, err) | ||
// Create a reconcile.Request | ||
req := reconcile.Request{ | ||
NamespacedName: types.NamespacedName{ | ||
Name: "telemetryservice", | ||
Namespace: "shifu-service", | ||
}, | ||
} | ||
|
||
// Create the reconciler | ||
reconciler := &TelemetryServiceReconciler{ | ||
Client: fakeClient, | ||
Scheme: scheme, | ||
} | ||
|
||
// Call the Reconcile function | ||
result, err := reconciler.Reconcile(context.Background(), req) | ||
// Assert that there were no errors | ||
assert.NoError(t, err) | ||
// Assert that the result does not requeue | ||
assert.False(t, result.Requeue) | ||
|
||
// Check if Deployment and Service instances were created | ||
deployment := &appsv1.Deployment{} | ||
err = fakeClient.Get(context.Background(), req.NamespacedName, deployment) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, deployment) | ||
|
||
service := &corev1.Service{} | ||
err = fakeClient.Get(context.Background(), req.NamespacedName, service) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, service) | ||
} |