github.com/nmstate/kubernetes-nmstate@v0.82.0/pkg/client/client.go (about)

     1  /*
     2  Copyright The Kubernetes NMState Authors.
     3  
     4  
     5  Licensed under the Apache License, Version 2.0 (the "License");
     6  you may not use this file except in compliance with the License.
     7  You may obtain a copy of the License at
     8  
     9      http://www.apache.org/licenses/LICENSE-2.0
    10  
    11  Unless required by applicable law or agreed to in writing, software
    12  distributed under the License is distributed on an "AS IS" BASIS,
    13  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  See the License for the specific language governing permissions and
    15  limitations under the License.
    16  */
    17  
    18  package client
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"fmt"
    24  	"os/exec"
    25  	"time"
    26  
    27  	"github.com/pkg/errors"
    28  	corev1 "k8s.io/api/core/v1"
    29  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"sigs.k8s.io/controller-runtime/pkg/client"
    32  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    33  
    34  	"github.com/nmstate/kubernetes-nmstate/api/names"
    35  	"github.com/nmstate/kubernetes-nmstate/api/shared"
    36  	nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1"
    37  	"github.com/nmstate/kubernetes-nmstate/pkg/nmstatectl"
    38  	"github.com/nmstate/kubernetes-nmstate/pkg/probe"
    39  )
    40  
    41  var (
    42  	log = logf.Log.WithName("client")
    43  )
    44  
    45  const (
    46  	defaultGwProbeTimeout = 120 * time.Second
    47  	apiServerProbeTimeout = 120 * time.Second
    48  	// DesiredStateConfigurationTimeout doubles the default gw ping probe and API server
    49  	// connectivity check timeout to ensure the Checkpoint is alive before rolling it back
    50  	// https://nmstate.github.io/cli_guide#manual-transaction-control
    51  	DesiredStateConfigurationTimeout = (defaultGwProbeTimeout + apiServerProbeTimeout) * 2
    52  )
    53  
    54  type DependencyVersions struct {
    55  	HandlerNmstateVersion string
    56  	HostNmstateVersion    string
    57  }
    58  
    59  func InitializeNodeNetworkState(cli client.Client, node *corev1.Node) (*nmstatev1beta1.NodeNetworkState, error) {
    60  	ownerRefList := []metav1.OwnerReference{{Name: node.ObjectMeta.Name, Kind: "Node", APIVersion: "v1", UID: node.UID}}
    61  
    62  	nodeNetworkState := nmstatev1beta1.NodeNetworkState{
    63  		// Create NodeNetworkState for this node
    64  		ObjectMeta: metav1.ObjectMeta{
    65  			Name:            node.ObjectMeta.Name,
    66  			OwnerReferences: ownerRefList,
    67  			Labels:          names.IncludeRelationshipLabels(nil),
    68  		},
    69  	}
    70  
    71  	err := cli.Create(context.TODO(), &nodeNetworkState)
    72  	if err != nil {
    73  		return nil, fmt.Errorf("error creating NodeNetworkState: %v, %+v", err, nodeNetworkState)
    74  	}
    75  
    76  	return &nodeNetworkState, nil
    77  }
    78  
    79  func CreateOrUpdateNodeNetworkState(
    80  	cli client.Client,
    81  	node *corev1.Node,
    82  	observedState shared.State,
    83  	nns *nmstatev1beta1.NodeNetworkState,
    84  	versions *DependencyVersions,
    85  ) error {
    86  	if nns == nil {
    87  		var err error
    88  		nns, err = InitializeNodeNetworkState(cli, node)
    89  		if err != nil {
    90  			return err
    91  		}
    92  	}
    93  	return UpdateCurrentState(cli, nns, observedState, versions)
    94  }
    95  
    96  func UpdateCurrentState(
    97  	cli client.Client,
    98  	nodeNetworkState *nmstatev1beta1.NodeNetworkState,
    99  	observedState shared.State,
   100  	versions *DependencyVersions,
   101  ) error {
   102  	if observedState.String() == nodeNetworkState.Status.CurrentState.String() {
   103  		log.Info("Skipping NodeNetworkState update, node network configuration not changed")
   104  		return nil
   105  	}
   106  
   107  	nodeNetworkState.Status.HandlerNmstateVersion = versions.HandlerNmstateVersion
   108  	nodeNetworkState.Status.HostNetworkManagerVersion = versions.HostNmstateVersion
   109  
   110  	nodeNetworkState.Status.CurrentState = observedState
   111  	nodeNetworkState.Status.LastSuccessfulUpdateTime = metav1.Time{Time: time.Now()}
   112  
   113  	err := cli.Status().Update(context.Background(), nodeNetworkState)
   114  	if err != nil {
   115  		if apierrors.IsNotFound(err) {
   116  			return errors.Wrap(err, "Request object not found, could have been deleted after reconcile request")
   117  		} else {
   118  			return errors.Wrap(err, "Error updating nodeNetworkState")
   119  		}
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func ExecuteCommand(command string, arguments ...string) (string, error) {
   126  	cmd := exec.Command(command, arguments...)
   127  	var stdout, stderr bytes.Buffer
   128  	cmd.Stdout = &stdout
   129  	cmd.Stderr = &stderr
   130  	if err := cmd.Run(); err != nil {
   131  		return "", fmt.Errorf("failed to execute %s: '%s', '%s', '%s'", command, err.Error(), stdout.String(), stderr.String())
   132  	}
   133  
   134  	return string(bytes.Trim(stdout.Bytes(), "\n")), nil
   135  }
   136  
   137  func rollback(cli client.Client, probes []probe.Probe, cause error) error {
   138  	message := fmt.Sprintf("rolling back desired state configuration: %s", cause)
   139  	err := nmstatectl.Rollback()
   140  	if err != nil {
   141  		return errors.Wrap(err, message)
   142  	}
   143  
   144  	// wait for system to settle after rollback
   145  	probesErr := probe.Run(cli, probes)
   146  	if probesErr != nil {
   147  		return errors.Wrap(errors.Wrap(probesErr, "failed running probes after rollback"), message)
   148  	}
   149  	return errors.New(message)
   150  }
   151  
   152  func ApplyDesiredState(cli client.Client, desiredState shared.State) (string, error) {
   153  	if string(desiredState.Raw) == "" {
   154  		return "Ignoring empty desired state", nil
   155  	}
   156  
   157  	// Before apply we get the probes that are working fine, they should be
   158  	// working fine after apply
   159  	probes := probe.Select(cli)
   160  
   161  	// Rollback before Apply to remove pending checkpoints (for example handler pod restarted
   162  	// before Commit)
   163  	nmstatectl.Rollback()
   164  
   165  	setOutput, err := nmstatectl.Set(desiredState, DesiredStateConfigurationTimeout)
   166  	if err != nil {
   167  		return setOutput, err
   168  	}
   169  
   170  	err = probe.Run(cli, probes)
   171  	if err != nil {
   172  		return "", rollback(cli, probes, errors.Wrap(err, "failed runnig probes after network changes"))
   173  	}
   174  
   175  	commitOutput, err := nmstatectl.Commit()
   176  	if err != nil {
   177  		// We cannot rollback if commit fails, just return the error
   178  		return commitOutput, err
   179  	}
   180  
   181  	commandOutput := fmt.Sprintf("setOutput: %s \n", setOutput)
   182  	return commandOutput, nil
   183  }