Revision cb53bb57e3c6327b2667f78321d45f48ace3fbc0 authored by Joe Stringer on 10 March 2021, 19:06:53 UTC, committed by Joe Stringer on 10 March 2021, 19:21:03 UTC
Signed-off-by: Joe Stringer <joe@cilium.io>
1 parent b564795
Raw File
directory.go
// Copyright 2018-2020 Authors of Cilium
//
// 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 endpoint

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/cilium/cilium/pkg/common"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/option"

	"github.com/sirupsen/logrus"
)

const (
	nextDirectorySuffix       = "_next"
	nextFailedDirectorySuffix = "_next_fail"
	backupDirectorySuffix     = "_stale"
)

// DirectoryPath returns the directory name for this endpoint bpf program.
func (e *Endpoint) DirectoryPath() string {
	return filepath.Join(".", fmt.Sprintf("%d", e.ID))
}

// FailedDirectoryPath returns the directory name for this endpoint bpf program
// failed builds.
func (e *Endpoint) FailedDirectoryPath() string {
	return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextFailedDirectorySuffix))
}

// StateDirectoryPath returns the directory name for this endpoint bpf program.
func (e *Endpoint) StateDirectoryPath() string {
	return filepath.Join(option.Config.StateDir, e.StringID())
}

// NextDirectoryPath returns the directory name for this endpoint bpf program
// next bpf builds.
func (e *Endpoint) NextDirectoryPath() string {
	return filepath.Join(".", fmt.Sprintf("%d%s", e.ID, nextDirectorySuffix))
}

func (e *Endpoint) backupDirectoryPath() string {
	return e.DirectoryPath() + backupDirectorySuffix
}

// synchronizeDirectories moves the files related to endpoint BPF program
// compilation to their according directories if compilation of BPF was
// necessary for the endpoint.
// Returns the original regenerationError if regenerationError was non-nil,
// or if any updates to directories for the endpoint's directories fails.
// Must be called with endpoint.mutex Lock()ed.
func (e *Endpoint) synchronizeDirectories(origDir string, stateDirComplete bool) error {
	scopedLog := e.getLogger()
	scopedLog.Debug("synchronizing directories")

	tmpDir := e.NextDirectoryPath()

	// Check if an existing endpoint directory exists, e.g.
	// /var/run/cilium/state/1111
	_, err := os.Stat(origDir)
	switch {

	// An endpoint directory already exists. We need to back it up before attempting
	// to move the new directory in its place so we can attempt recovery.
	case !os.IsNotExist(err):
		scopedLog.Debug("endpoint directory exists; backing it up")
		backupDir := e.backupDirectoryPath()

		// Remove any eventual old backup directory. This may fail if
		// the directory does not exist. The error is deliberately
		// ignored.
		e.removeDirectory(backupDir)

		// Move the current endpoint directory to a backup location
		scopedLog.WithFields(logrus.Fields{
			"originalDirectory": origDir,
			"backupDirectory":   backupDir,
		}).Debug("moving current directory to backup location")
		if err := os.Rename(origDir, backupDir); err != nil {
			return fmt.Errorf("unable to rename current endpoint directory: %s", err)
		}

		// Regarldess of whether the atomic replace succeeds or not,
		// ensure that the backup directory is removed when the
		// function returns.
		defer e.removeDirectory(backupDir)

		// Make temporary directory the new endpoint directory
		if err := os.Rename(tmpDir, origDir); err != nil {
			if err2 := os.Rename(backupDir, origDir); err2 != nil {
				scopedLog.WithFields(logrus.Fields{
					logfields.Path: backupDir,
				}).Warn("restoring directory for endpoint failed, endpoint " +
					"is in inconsistent state. Keeping stale directory.")
				return err2
			}

			return fmt.Errorf("restored original endpoint directory, atomic directory move failed: %s", err)
		}

		// If the compilation was skipped then we need to copy the old
		// bpf objects into the new directory
		if !stateDirComplete {
			scopedLog.Debug("some BPF state files were not recreated; moving old BPF objects into new directory")
			err := common.MoveNewFilesTo(backupDir, origDir)
			if err != nil {
				log.WithError(err).Debugf("unable to copy old bpf object "+
					"files from %s into the new directory %s.", backupDir, origDir)
			}
		}

	// No existing endpoint directory, synchronizing the directory is a
	// simple move
	default:
		// Make temporary directory the new endpoint directory
		scopedLog.WithFields(logrus.Fields{
			"temporaryDirectory": tmpDir,
			"originalDirectory":  origDir,
		}).Debug("attempting to make temporary directory new directory for endpoint programs")
		if err := os.Rename(tmpDir, origDir); err != nil {
			return fmt.Errorf("atomic endpoint directory move failed: %s", err)
		}
	}

	// The build succeeded and is in place, any eventual existing failure
	// directory can be removed.
	e.removeDirectory(e.FailedDirectoryPath())

	return nil
}

func (e *Endpoint) removeDirectory(path string) error {
	e.getLogger().WithField("directory", path).Debug("removing directory")
	return os.RemoveAll(path)
}

func (e *Endpoint) removeDirectories() {
	e.removeDirectory(e.DirectoryPath())
	e.removeDirectory(e.FailedDirectoryPath())
	e.removeDirectory(e.NextDirectoryPath())
	e.removeDirectory(e.backupDirectoryPath())
}
back to top