-
Notifications
You must be signed in to change notification settings - Fork 260
Request controller for CNS #583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
pjohnst5
merged 22 commits into
Azure:master
from
pjohnst5:request-controller-reconcile-loop
Jul 2, 2020
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
ec63812
NewRequestController and StartRequestController
pjohnst5 e861bca
Making Start Manager in go routine
pjohnst5 086a64b
Lookup HOSTNAME env var
pjohnst5 82fa79d
Adding cnsipaminterface.go
pjohnst5 6a3edf3
Created requestController interface and implemented updating CRD
pjohnst5 c3321b7
fix windows 1903 test apimodel.json (#585)
matmerr 887ab16
Avoiding redundant calls into cns by only watching for status updates…
pjohnst5 c56e1e7
fixing comments
pjohnst5 2eb5279
Cleaned up code and added more comments
pjohnst5 c817700
Made client interface for testing purposes and changed structure of f…
pjohnst5 52c7268
Addressed comments from Paul Miller and Wei
pjohnst5 109e605
Beginning unit tests
pjohnst5 c66d5db
resolved conflict by keeping Matt's change of adding nnc as short nam…
pjohnst5 8786e2f
Finished unit tests
pjohnst5 c63dc59
Merge branch 'master' into request-controller-reconcile-loop
pjohnst5 76c1b66
Fixing pipeline issues
pjohnst5 20386ca
found issue, fixed HOSTNAME environment variable dependency
pjohnst5 9991698
review changes requested
pjohnst5 588f39e
more review changes
pjohnst5 6ef0a7a
Addressed changes from yesterday's review
pjohnst5 9a9f3ce
Changing makefile line to run correct package
pjohnst5 72ad988
Addressed Matt Long's suggestions
pjohnst5 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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,8 @@ | ||
| package cnsclient | ||
|
|
||
| import "github.com/Azure/azure-container-networking/cns" | ||
|
|
||
| // APIClient interface to update cns state | ||
| type APIClient interface { | ||
| UpdateCNSState(createNetworkContainerRequest *cns.CreateNetworkContainerRequest) error | ||
| } |
This file contains hidden or 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,17 @@ | ||
| package httpapi | ||
|
|
||
| import ( | ||
| "github.com/Azure/azure-container-networking/cns" | ||
| "github.com/Azure/azure-container-networking/cns/restserver" | ||
| ) | ||
|
|
||
| // Client implements APIClient interface. Used to update CNS state | ||
| type Client struct { | ||
| RestService *restserver.HTTPRestService | ||
| } | ||
|
|
||
| // UpdateCNSState updates cns state | ||
| func (client *Client) UpdateCNSState(createNetworkContainerRequest *cns.CreateNetworkContainerRequest) error { | ||
| //Mat will pick up from here | ||
| return nil | ||
| } |
This file contains hidden or 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,83 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "time" | ||
|
|
||
| "github.com/Azure/azure-container-networking/cns/logger" | ||
| "github.com/Azure/azure-container-networking/cns/requestcontroller" | ||
| "github.com/Azure/azure-container-networking/cns/requestcontroller/kubecontroller" | ||
| "github.com/Azure/azure-container-networking/cns/restserver" | ||
| nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha" | ||
| "golang.org/x/net/context" | ||
| ) | ||
|
|
||
| func goRequestController(rc requestcontroller.RequestController) { | ||
| //Exit channel for requestController, this channel is notified when requestController receives | ||
| //SIGINT or SIGTERM, requestControllerExitChan is sent 'true' and you can clean up anything then | ||
| requestControllerExitChan := make(chan bool, 1) | ||
|
|
||
| //Start the RequestController which starts the reconcile loop, blocks | ||
| go func() { | ||
| if err := rc.StartRequestController(requestControllerExitChan); err != nil { | ||
| logger.Errorf("Error starting requestController: %v", err) | ||
| return | ||
| } | ||
| }() | ||
|
|
||
| // After calling StartRequestController, there needs to be some pause before updating CRD spec | ||
| time.Sleep(5 * time.Second) | ||
|
|
||
| // We provide a context when making operations on CRD in case we need to cancel operation | ||
| cntxt := context.Background() | ||
|
|
||
| // Create some dummy uuids | ||
| uuids := make([]string, 5) | ||
| uuids[0] = "uuid0" | ||
| uuids[1] = "uuid1" | ||
| uuids[2] = "uuid2" | ||
| uuids[3] = "uuid3" | ||
| uuids[4] = "uuid4" | ||
|
|
||
| // newCount = oldCount - ips releasing | ||
| // In this example, say we had 20 allocated to the node, we want to release 5, new count would be 15 | ||
| oldCount := 20 | ||
| newRequestedIPCount := int64(oldCount - len(uuids)) | ||
|
|
||
| //Create CRD spec | ||
| spec := &nnc.NodeNetworkConfigSpec{ | ||
| RequestedIPCount: newRequestedIPCount, | ||
| IPsNotInUse: uuids, | ||
| } | ||
|
|
||
| //Update CRD spec | ||
| rc.UpdateCRDSpec(cntxt, spec) | ||
|
|
||
| <-requestControllerExitChan | ||
| logger.Printf("Request controller received sigint or sigterm, time to cleanup") | ||
| // Clean clean... | ||
| } | ||
|
|
||
| //Example of using the requestcontroller package | ||
| func main() { | ||
| var requestController requestcontroller.RequestController | ||
|
|
||
| //Assuming logger is already setup and stuff | ||
| logger.InitLogger("Azure CNS", 3, 3, "") | ||
|
|
||
| restService := &restserver.HTTPRestService{} | ||
|
|
||
| //Provide kubeconfig, this method was abstracted out for testing | ||
| kubeconfig, err := kubecontroller.GetKubeConfig() | ||
| if err != nil { | ||
| logger.Errorf("Error getting kubeconfig: %v", err) | ||
| } | ||
|
|
||
| requestController, err = kubecontroller.NewCrdRequestController(restService, kubeconfig) | ||
| if err != nil { | ||
| logger.Errorf("Error making new RequestController: %v", err) | ||
| return | ||
| } | ||
|
|
||
| //Rely on the interface | ||
| goRequestController(requestController) | ||
| } |
18 changes: 18 additions & 0 deletions
18
cns/requestcontroller/kubecontroller/apiclientinterface.go
This file contains hidden or 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,18 @@ | ||
| package kubecontroller | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "k8s.io/apimachinery/pkg/runtime" | ||
| "k8s.io/apimachinery/pkg/types" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| ) | ||
|
|
||
| // ObjectKey identifies a Kubernetes Object. | ||
| type ObjectKey = types.NamespacedName | ||
|
|
||
| // KubeClient is an interface that talks to the API server | ||
| type KubeClient interface { | ||
| Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error | ||
| Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error | ||
| } | ||
This file contains hidden or 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 kubecontroller | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/Azure/azure-container-networking/cns/cnsclient" | ||
| "github.com/Azure/azure-container-networking/cns/logger" | ||
| nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
| ctrl "sigs.k8s.io/controller-runtime" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| "sigs.k8s.io/controller-runtime/pkg/reconcile" | ||
| ) | ||
|
|
||
| // CrdReconciler watches for CRD status changes | ||
| type CrdReconciler struct { | ||
| KubeClient KubeClient | ||
| NodeName string | ||
| CNSClient cnsclient.APIClient | ||
| } | ||
|
|
||
| // Reconcile is called on CRD status changes | ||
| func (r *CrdReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { | ||
| var nodeNetConfig nnc.NodeNetworkConfig | ||
|
|
||
| //Get the CRD object | ||
| if err := r.KubeClient.Get(context.TODO(), request.NamespacedName, &nodeNetConfig); err != nil { | ||
| if apierrors.IsNotFound(err) { | ||
| logger.Printf("[cns-rc] CRD not found, ignoring %v", err) | ||
| return reconcile.Result{}, client.IgnoreNotFound(err) | ||
| } else { | ||
| logger.Errorf("[cns-rc] Error retrieving CRD from cache : %v", err) | ||
| return reconcile.Result{}, err | ||
| } | ||
| } | ||
|
|
||
| logger.Printf("[cns-rc] CRD object: %v", nodeNetConfig) | ||
|
|
||
| //TODO: Translate CRD status into NetworkContainer request | ||
| ncRequest, err := CRDStatusToNCRequest(&nodeNetConfig.Status) | ||
| if err != nil { | ||
| logger.Errorf("[cns-rc] Error translating crd status to nc request %v", err) | ||
| //requeue | ||
| return reconcile.Result{}, err | ||
| } | ||
|
|
||
| //TODO: process the nc request on CNS side | ||
| r.CNSClient.UpdateCNSState(ncRequest) | ||
|
|
||
| return reconcile.Result{}, nil | ||
| } | ||
|
|
||
| // SetupWithManager Sets up the reconciler with a new manager, filtering using NodeNetworkConfigFilter | ||
| func (r *CrdReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
| return ctrl.NewControllerManagedBy(mgr). | ||
| For(&nnc.NodeNetworkConfig{}). | ||
| WithEventFilter(NodeNetworkConfigFilter{nodeName: r.NodeName}). | ||
| Complete(r) | ||
| } |
169 changes: 169 additions & 0 deletions
169
cns/requestcontroller/kubecontroller/crdrequestcontroller.go
This file contains hidden or 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,169 @@ | ||
| package kubecontroller | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "os" | ||
|
|
||
| "github.com/Azure/azure-container-networking/cns/cnsclient/httpapi" | ||
| "github.com/Azure/azure-container-networking/cns/logger" | ||
| "github.com/Azure/azure-container-networking/cns/restserver" | ||
| nnc "github.com/Azure/azure-container-networking/nodenetworkconfig/api/v1alpha" | ||
| "k8s.io/apimachinery/pkg/runtime" | ||
| clientgoscheme "k8s.io/client-go/kubernetes/scheme" | ||
| "k8s.io/client-go/rest" | ||
| ctrl "sigs.k8s.io/controller-runtime" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| "sigs.k8s.io/controller-runtime/pkg/manager" | ||
| ) | ||
|
|
||
| const nodeNameEnvVar = "NODENAME" | ||
| const k8sNamespace = "kube-system" | ||
| const prometheusAddress = "0" //0 means disabled | ||
|
|
||
| // crdRequestController | ||
| // - watches CRD status changes | ||
| // - updates CRD spec | ||
| type crdRequestController struct { | ||
| mgr manager.Manager //Manager starts the reconcile loop which watches for crd status changes | ||
| KubeClient KubeClient //KubeClient interacts with API server | ||
| nodeName string //name of node running this program | ||
| Reconciler *CrdReconciler | ||
| } | ||
|
|
||
| // GetKubeConfig precedence | ||
| // * --kubeconfig flag pointing at a file at this cmd line | ||
| // * KUBECONFIG environment variable pointing at a file | ||
| // * In-cluster config if running in cluster | ||
| // * $HOME/.kube/config if exists | ||
| func GetKubeConfig() (*rest.Config, error) { | ||
| k8sconfig, err := ctrl.GetConfig() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return k8sconfig, nil | ||
| } | ||
|
|
||
| //NewCrdRequestController given a reference to CNS's HTTPRestService state, returns a crdRequestController struct | ||
| func NewCrdRequestController(restService *restserver.HTTPRestService, kubeconfig *rest.Config) (*crdRequestController, error) { | ||
|
|
||
| //Check that logger package has been intialized | ||
| if logger.Log == nil { | ||
| return nil, errors.New("Must initialize logger before calling") | ||
pjohnst5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // Check that NODENAME environment variable is set. NODENAME is name of node running this program | ||
| nodeName := os.Getenv(nodeNameEnvVar) | ||
| if nodeName == "" { | ||
| return nil, errors.New("Must declare " + nodeNameEnvVar + " environment variable.") | ||
| } | ||
|
|
||
| //Add client-go scheme to runtime sheme so manager can recognize it | ||
| var scheme = runtime.NewScheme() | ||
| if err := clientgoscheme.AddToScheme(scheme); err != nil { | ||
| return nil, errors.New("Error adding client-go scheme to runtime scheme") | ||
| } | ||
|
|
||
| //Add CRD scheme to runtime sheme so manager can recognize it | ||
| if err := nnc.AddToScheme(scheme); err != nil { | ||
| return nil, errors.New("Error adding NodeNetworkConfig scheme to runtime scheme") | ||
| } | ||
|
|
||
| // Create manager for CrdRequestController | ||
| // MetricsBindAddress is the tcp address that the controller should bind to | ||
| // for serving prometheus metrics, set to "0" to disable | ||
| mgr, err := ctrl.NewManager(kubeconfig, ctrl.Options{ | ||
| Scheme: scheme, | ||
| MetricsBindAddress: prometheusAddress, | ||
| Namespace: k8sNamespace, | ||
| }) | ||
| if err != nil { | ||
| logger.Errorf("[cns-rc] Error creating new request controller manager: %v", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| //Create httpClient | ||
| httpClient := &httpapi.Client{ | ||
| RestService: restService, | ||
| } | ||
|
|
||
| //Create reconciler | ||
| crdreconciler := &CrdReconciler{ | ||
| KubeClient: mgr.GetClient(), | ||
| NodeName: nodeName, | ||
| CNSClient: httpClient, | ||
| } | ||
|
|
||
| // Setup manager with reconciler | ||
| if err := crdreconciler.SetupWithManager(mgr); err != nil { | ||
| logger.Errorf("[cns-rc] Error creating new CrdRequestController: %v", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| // Create the requestController | ||
| crdRequestController := crdRequestController{ | ||
| mgr: mgr, | ||
| KubeClient: mgr.GetClient(), | ||
| nodeName: nodeName, | ||
| Reconciler: crdreconciler, | ||
| } | ||
|
|
||
| return &crdRequestController, nil | ||
| } | ||
|
|
||
| // StartRequestController starts the Reconciler loop which watches for CRD status updates | ||
| // Blocks until SIGINT or SIGTERM is received | ||
| // Notifies exitChan when kill signal received | ||
| func (crdRC *crdRequestController) StartRequestController(exitChan chan bool) error { | ||
| logger.Printf("Starting manager") | ||
| if err := crdRC.mgr.Start(SetupSignalHandler(exitChan)); err != nil { | ||
| logger.Errorf("[cns-rc] Error starting manager: %v", err) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // UpdateCRDSpec updates the CRD spec | ||
| func (crdRC *crdRequestController) UpdateCRDSpec(cntxt context.Context, crdSpec *nnc.NodeNetworkConfigSpec) error { | ||
| nodeNetworkConfig, err := crdRC.getNodeNetConfig(cntxt, crdRC.nodeName, k8sNamespace) | ||
| if err != nil { | ||
| logger.Errorf("[cns-rc] Error getting CRD when updating spec %v", err) | ||
| return err | ||
| } | ||
|
|
||
| //Update the CRD spec | ||
| crdSpec.DeepCopyInto(&nodeNetworkConfig.Spec) | ||
|
|
||
| //Send update to API server | ||
| if err := crdRC.updateNodeNetConfig(cntxt, nodeNetworkConfig); err != nil { | ||
| logger.Errorf("[cns-rc] Error updating CRD spec %v", err) | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // getNodeNetConfig gets the nodeNetworkConfig CRD given the name and namespace of the CRD object | ||
| func (crdRC *crdRequestController) getNodeNetConfig(cntxt context.Context, name, namespace string) (*nnc.NodeNetworkConfig, error) { | ||
| nodeNetworkConfig := &nnc.NodeNetworkConfig{} | ||
|
|
||
| err := crdRC.KubeClient.Get(cntxt, client.ObjectKey{ | ||
| Namespace: namespace, | ||
| Name: name, | ||
| }, nodeNetworkConfig) | ||
|
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return nodeNetworkConfig, nil | ||
| } | ||
|
|
||
| // updateNodeNetConfig updates the nodeNetConfig object in the API server with the given nodeNetworkConfig object | ||
| func (crdRC *crdRequestController) updateNodeNetConfig(cntxt context.Context, nodeNetworkConfig *nnc.NodeNetworkConfig) error { | ||
| if err := crdRC.KubeClient.Update(cntxt, nodeNetworkConfig); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.