internal/kubelet/install.go (112 lines of code) (raw):

package kubelet import ( "bytes" "context" _ "embed" stdErrors "errors" "os" "path" "path/filepath" "github.com/pkg/errors" "go.uber.org/zap" "github.com/aws/eks-hybrid/internal/artifact" "github.com/aws/eks-hybrid/internal/tracker" ) const ( // DefaultBinPath is the path to the Kubelet binary. BinPath = "/usr/bin/kubelet" // UnitPath is the path to the Kubelet systemd unit file. UnitPath = "/etc/systemd/system/kubelet.service" artifactName = "kubelet" artifactFilePerms = 0o755 ) var KubeletCurrentCertPath = path.Join(kubeconfigRoot, "pki", "kubelet-server-current.pem") //go:embed kubelet.service var kubeletUnitFile []byte // Source represents a source that serves a kubelet binary. type Source interface { GetKubelet(context.Context) (artifact.Source, error) } type InstallOptions struct { InstallRoot string Tracker *tracker.Tracker Source Source Logger *zap.Logger } // Install installs kubelet at BinPath and installs a systemd unit file at UnitPath. The systemd // unit is configured to launch the kubelet binary. func Install(ctx context.Context, opts InstallOptions) error { if err := installFromSource(ctx, opts); err != nil { return errors.Wrap(err, "installing kubelet") } if err := installSystemdUnit(filepath.Join(opts.InstallRoot, UnitPath)); err != nil { return errors.Wrap(err, "installing systemd unit") } if err := opts.Tracker.Add(artifact.Kubelet); err != nil { return errors.Wrap(err, "adding kubelet to tracker") } return nil } func installFromSource(ctx context.Context, opts InstallOptions) error { // Retry up to 3 times to download and validate the checksum var err error for range 3 { err = downloadFileTo(ctx, opts) if err == nil { break } opts.Logger.Error("Downloading kubelet failed. Retrying...", zap.Error(err)) } return err } func downloadFileTo(ctx context.Context, opts InstallOptions) error { kubelet, err := opts.Source.GetKubelet(ctx) if err != nil { return errors.Wrap(err, "getting kubelet source") } defer kubelet.Close() if err := artifact.InstallFile(filepath.Join(opts.InstallRoot, BinPath), kubelet, artifactFilePerms); err != nil { return errors.Wrap(err, "installing kubelet") } if !kubelet.VerifyChecksum() { return errors.Errorf("kubelet checksum mismatch: %v", artifact.NewChecksumError(kubelet)) } return nil } func installSystemdUnit(unitPath string) error { buf := bytes.NewBuffer(kubeletUnitFile) if err := artifact.InstallFile(unitPath, buf, 0o644); err != nil { return errors.Errorf("failed to install kubelet systemd unit: %v", err) } return nil } type UninstallOptions struct { // InstallRoot is optionally the root directory of the installation // If not provided, the default will be / InstallRoot string } func Uninstall(opts UninstallOptions) error { pathsToRemove := []string{ filepath.Join(opts.InstallRoot, BinPath), filepath.Join(opts.InstallRoot, UnitPath), filepath.Join(opts.InstallRoot, kubeconfigPath), filepath.Join(opts.InstallRoot, path.Dir(kubeletConfigRoot)), filepath.Join(opts.InstallRoot, KubeletCurrentCertPath), } allErrors := []error{} // resolve the symlink and add actual file to remove actualCertPath, err := filepath.EvalSymlinks(filepath.Join(opts.InstallRoot, KubeletCurrentCertPath)) if err != nil && !os.IsNotExist(err) { allErrors = append(allErrors, errors.Wrap(err, "resolving symlink for kubelet cert")) } if actualCertPath != "" { pathsToRemove = append(pathsToRemove, actualCertPath) } for _, path := range pathsToRemove { if err := os.RemoveAll(path); err != nil { allErrors = append(allErrors, err) } } if len(allErrors) > 0 { return stdErrors.Join(allErrors...) } return nil } func Upgrade(ctx context.Context, src Source, log *zap.Logger) error { kubelet, err := src.GetKubelet(ctx) if err != nil { return errors.Wrap(err, "getting kubelet source") } defer kubelet.Close() return artifact.Upgrade(artifactName, BinPath, kubelet, artifactFilePerms, log) }