Skip to content

Commit

Permalink
[teleport-update] Support for Enterprise/FIPS migration (#49451)
Browse files Browse the repository at this point in the history
* store ent/fips data

cleanup

formatting

revert updater rename

cleanup

Update lib/autoupdate/agent/config.go

Co-authored-by: Zac Bergquist <[email protected]>

feedback

* feedback

* feedback

* lint
  • Loading branch information
sclevine authored Dec 4, 2024
1 parent 7eeed25 commit d1b5f6c
Show file tree
Hide file tree
Showing 43 changed files with 766 additions and 394 deletions.
2 changes: 1 addition & 1 deletion constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ const (
// ComponentProxySecureGRPC represents a secure gRPC server running on Proxy (used for Kube).
ComponentProxySecureGRPC = "proxy:secure-grpc"

// ComponentUpdater represents the agent updater.
// ComponentUpdater represents the teleport-update binary.
ComponentUpdater = "updater"

// VerboseLogsEnvVar forces all logs to be verbose (down to DEBUG level)
Expand Down
150 changes: 136 additions & 14 deletions lib/autoupdate/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
package agent

import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"strings"
Expand Down Expand Up @@ -67,12 +69,87 @@ type UpdateSpec struct {

// UpdateStatus describes the status field in update.yaml.
type UpdateStatus struct {
// ActiveVersion is the currently active Teleport version.
ActiveVersion string `yaml:"active_version"`
// BackupVersion is the last working version of Teleport.
BackupVersion string `yaml:"backup_version"`
// SkipVersion is the last reverted version of Teleport.
SkipVersion string `yaml:"skip_version,omitempty"`
// Active is the currently active revision of Teleport.
Active Revision `yaml:"active"`
// Backup is the last working revision of Teleport.
Backup *Revision `yaml:"backup,omitempty"`
// Skip is the skipped revision of Teleport.
// Skipped revisions are not applied because they
// are known to crash.
Skip *Revision `yaml:"skip,omitempty"`
}

// Revision is a version and edition of Teleport.
type Revision struct {
// Version is the version of Teleport.
Version string `yaml:"version" json:"version"`
// Flags describe the edition of Teleport.
Flags InstallFlags `yaml:"flags,flow,omitempty" json:"flags,omitempty"`
}

// NewRevision create a Revision.
// If version is not set, no flags are returned.
// This ensures that all Revisions without versions are zero-valued.
func NewRevision(version string, flags InstallFlags) Revision {
if version != "" {
return Revision{
Version: version,
Flags: flags,
}
}
return Revision{}
}

// NewRevisionFromDir translates a directory path containing Teleport into a Revision.
func NewRevisionFromDir(dir string) (Revision, error) {
parts := strings.Split(dir, "_")
var out Revision
if len(parts) == 0 {
return out, trace.Errorf("dir name empty")
}
out.Version = parts[0]
if out.Version == "" {
return out, trace.Errorf("version missing in dir %s", dir)
}
switch flags := parts[1:]; len(flags) {
case 2:
if flags[1] != FlagFIPS.DirFlag() {
break
}
out.Flags |= FlagFIPS
fallthrough
case 1:
if flags[0] != FlagEnterprise.DirFlag() {
break
}
out.Flags |= FlagEnterprise
fallthrough
case 0:
return out, nil
}
return out, trace.Errorf("invalid flag in %s", dir)
}

// Dir returns the directory path name of a Revision.
func (r Revision) Dir() string {
// Do not change the order of these statements.
// Otherwise, installed versions will no longer match update.yaml.
var suffix string
if r.Flags&(FlagEnterprise|FlagFIPS) != 0 {
suffix += "_" + FlagEnterprise.DirFlag()
}
if r.Flags&FlagFIPS != 0 {
suffix += "_" + FlagFIPS.DirFlag()
}
return r.Version + suffix
}

// String returns a human-readable description of a Teleport revision.
func (r Revision) String() string {
if flags := r.Flags.Strings(); len(flags) > 0 {
return fmt.Sprintf("%s+%s", r.Version, strings.Join(flags, "+"))
}
return r.Version
}

// readConfig reads UpdateConfig from a file.
Expand All @@ -93,10 +170,10 @@ func readConfig(path string) (*UpdateConfig, error) {
return nil, trace.Wrap(err, "failed to parse")
}
if k := cfg.Kind; k != updateConfigKind {
return nil, trace.Errorf("invalid kind %q", k)
return nil, trace.Errorf("invalid kind %s", k)
}
if v := cfg.Version; v != updateConfigVersion {
return nil, trace.Errorf("invalid version %q", v)
return nil, trace.Errorf("invalid version %s", v)
}
return &cfg, nil
}
Expand Down Expand Up @@ -155,10 +232,8 @@ type Status struct {

// FindResp summarizes the auto-update status response from cluster.
type FindResp struct {
// Version of Teleport to install
TargetVersion string `yaml:"target_version"`
// Flags describing the edition of Teleport
Flags InstallFlags `yaml:"flags"`
// Target revision of Teleport to install
Target Revision `yaml:"target"`
// InWindow is true when the install should happen now.
InWindow bool `yaml:"in_window"`
// Jitter duration before an automated install
Expand All @@ -175,10 +250,23 @@ const (
FlagFIPS
)

func (i InstallFlags) MarshalYAML() (any, error) {
return i.Strings(), nil
// NewInstallFlagsFromStrings returns InstallFlags given a slice of human-readable strings.
func NewInstallFlagsFromStrings(s []string) InstallFlags {
var out InstallFlags
for _, f := range s {
for _, flag := range []InstallFlags{
FlagEnterprise,
FlagFIPS,
} {
if f == flag.String() {
out |= flag
}
}
}
return out
}

// Strings converts InstallFlags to a slice of human-readable strings.
func (i InstallFlags) Strings() []string {
var out []string
for _, flag := range []InstallFlags{
Expand All @@ -192,6 +280,7 @@ func (i InstallFlags) Strings() []string {
return out
}

// String returns the string representation of a single InstallFlag flag, or "Unknown".
func (i InstallFlags) String() string {
switch i {
case 0:
Expand All @@ -203,3 +292,36 @@ func (i InstallFlags) String() string {
}
return "Unknown"
}

// DirFlag returns the directory path representation of a single InstallFlag flag, or "unknown".
func (i InstallFlags) DirFlag() string {
switch i {
case 0:
return ""
case FlagEnterprise:
return "ent"
case FlagFIPS:
return "fips"
}
return "unknown"
}

func (i InstallFlags) MarshalYAML() (any, error) {
return i.Strings(), nil
}

func (i InstallFlags) MarshalJSON() ([]byte, error) {
return json.Marshal(i.Strings())
}

func (i *InstallFlags) UnmarshalYAML(n *yaml.Node) error {
var s []string
if err := n.Decode(&s); err != nil {
return trace.Wrap(err)
}
if i == nil {
return trace.BadParameter("nil install flags while parsing YAML")
}
*i = NewInstallFlagsFromStrings(s)
return nil
}
Loading

0 comments on commit d1b5f6c

Please sign in to comment.