github.com/fafucoder/cilium@v1.6.11/pkg/endpoint/directory.go (about)

     1  // Copyright 2018-2020 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package endpoint
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"github.com/cilium/cilium/common"
    23  	"github.com/cilium/cilium/pkg/logging/logfields"
    24  	"github.com/cilium/cilium/pkg/option"
    25  
    26  	"github.com/sirupsen/logrus"
    27  )
    28  
    29  const (
    30  	nextDirectorySuffix       = "_next"
    31  	nextFailedDirectorySuffix = "_next_fail"
    32  	backupDirectorySuffix     = "_stale"
    33  )
    34  
    35  // DirectoryPath returns the directory name for this endpoint bpf program.
    36  func (e *Endpoint) DirectoryPath() string {
    37  	return filepath.Join(".", fmt.Sprintf("%d", e.ID))
    38  }
    39  
    40  // FailedDirectoryPath returns the directory name for this endpoint bpf program
    41  // failed builds.
    42  func (e *Endpoint) FailedDirectoryPath() string {
    43  	return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextFailedDirectorySuffix))
    44  }
    45  
    46  // StateDirectoryPath returns the directory name for this endpoint bpf program.
    47  func (e *Endpoint) StateDirectoryPath() string {
    48  	return filepath.Join(option.Config.StateDir, e.StringID())
    49  }
    50  
    51  // NextDirectoryPath returns the directory name for this endpoint bpf program
    52  // next bpf builds.
    53  func (e *Endpoint) NextDirectoryPath() string {
    54  	return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextDirectorySuffix))
    55  }
    56  
    57  func (e *Endpoint) backupDirectoryPath() string {
    58  	return e.DirectoryPath() + backupDirectorySuffix
    59  }
    60  
    61  // synchronizeDirectories moves the files related to endpoint BPF program
    62  // compilation to their according directories if compilation of BPF was
    63  // necessary for the endpoint.
    64  // Returns the original regenerationError if regenerationError was non-nil,
    65  // or if any updates to directories for the endpoint's directories fails.
    66  // Must be called with endpoint.Mutex held.
    67  func (e *Endpoint) synchronizeDirectories(origDir string, stateDirComplete bool) error {
    68  	scopedLog := e.getLogger()
    69  	scopedLog.Debug("synchronizing directories")
    70  
    71  	tmpDir := e.NextDirectoryPath()
    72  
    73  	// Check if an existing endpoint directory exists, e.g.
    74  	// /var/run/cilium/state/1111
    75  	_, err := os.Stat(origDir)
    76  	switch {
    77  
    78  	// An endpoint directory already exists. We need to back it up before attempting
    79  	// to move the new directory in its place so we can attempt recovery.
    80  	case !os.IsNotExist(err):
    81  		scopedLog.Debug("endpoint directory exists; backing it up")
    82  		backupDir := e.backupDirectoryPath()
    83  
    84  		// Remove any eventual old backup directory. This may fail if
    85  		// the directory does not exist. The error is deliberately
    86  		// ignored.
    87  		e.removeDirectory(backupDir)
    88  
    89  		// Move the current endpoint directory to a backup location
    90  		scopedLog.WithFields(logrus.Fields{
    91  			"originalDirectory": origDir,
    92  			"backupDirectory":   backupDir,
    93  		}).Debug("moving current directory to backup location")
    94  		if err := os.Rename(origDir, backupDir); err != nil {
    95  			return fmt.Errorf("unable to rename current endpoint directory: %s", err)
    96  		}
    97  
    98  		// Regarldess of whether the atomic replace succeeds or not,
    99  		// ensure that the backup directory is removed when the
   100  		// function returns.
   101  		defer e.removeDirectory(backupDir)
   102  
   103  		// Make temporary directory the new endpoint directory
   104  		if err := os.Rename(tmpDir, origDir); err != nil {
   105  			if err2 := os.Rename(backupDir, origDir); err2 != nil {
   106  				scopedLog.WithFields(logrus.Fields{
   107  					logfields.Path: backupDir,
   108  				}).Warn("restoring directory for endpoint failed, endpoint " +
   109  					"is in inconsistent state. Keeping stale directory.")
   110  				return err2
   111  			}
   112  
   113  			return fmt.Errorf("restored original endpoint directory, atomic directory move failed: %s", err)
   114  		}
   115  
   116  		// If the compilation was skipped then we need to copy the old
   117  		// bpf objects into the new directory
   118  		if !stateDirComplete {
   119  			scopedLog.Debug("some BPF state files were not recreated; moving old BPF objects into new directory")
   120  			err := common.MoveNewFilesTo(backupDir, origDir)
   121  			if err != nil {
   122  				log.WithError(err).Debugf("unable to copy old bpf object "+
   123  					"files from %s into the new directory %s.", backupDir, origDir)
   124  			}
   125  		}
   126  
   127  	// No existing endpoint directory, synchronizing the directory is a
   128  	// simple move
   129  	default:
   130  		// Make temporary directory the new endpoint directory
   131  		scopedLog.WithFields(logrus.Fields{
   132  			"temporaryDirectory": tmpDir,
   133  			"originalDirectory":  origDir,
   134  		}).Debug("attempting to make temporary directory new directory for endpoint programs")
   135  		if err := os.Rename(tmpDir, origDir); err != nil {
   136  			return fmt.Errorf("atomic endpoint directory move failed: %s", err)
   137  		}
   138  	}
   139  
   140  	// The build succeeded and is in place, any eventual existing failure
   141  	// directory can be removed.
   142  	e.removeDirectory(e.FailedDirectoryPath())
   143  
   144  	return nil
   145  }
   146  
   147  func (e *Endpoint) removeDirectory(path string) error {
   148  	e.getLogger().WithField("directory", path).Debug("removing directory")
   149  	return os.RemoveAll(path)
   150  }
   151  
   152  func (e *Endpoint) removeDirectories() {
   153  	e.removeDirectory(e.DirectoryPath())
   154  	e.removeDirectory(e.FailedDirectoryPath())
   155  	e.removeDirectory(e.NextDirectoryPath())
   156  	e.removeDirectory(e.backupDirectoryPath())
   157  }