github.com/openshift/installer@v1.4.17/pkg/version/version.go (about)

     1  // Package version includes the version information for installer.
     2  package version
     3  
     4  import (
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/sirupsen/logrus"
    10  
    11  	"github.com/openshift/installer/pkg/types"
    12  )
    13  
    14  // This file handles correctly identifying the default release version, which is expected to be
    15  // replaced in the binary post-compile by the release name extracted from a payload. The expected modification is:
    16  //
    17  // 1. Extract a release binary from the installer image referenced within the release image
    18  // 2. Identify the release name, add a NUL terminator byte (0x00) to the end, calculate length
    19  // 3. Length must be less than the marker length
    20  // 4. Search through the installer binary looking for `\x00_RELEASE_VERSION_LOCATION_\x00<PADDING_TO_LENGTH>`
    21  //    where padding is the ASCII character X and length is the total length of the marker
    22  // 5. Overwrite the beginning of the marker with the release name and a NUL terminator byte (0x00)
    23  
    24  var (
    25  	// Raw is the string representation of the version. This will be replaced
    26  	// with the calculated version at build time.
    27  	// set in hack/build.sh
    28  	Raw = "was not built correctly"
    29  
    30  	// Commit is the commit hash from which the installer was built.
    31  	// Set in hack/build.sh.
    32  	Commit = ""
    33  
    34  	// defaultArch is the payload architecture for which the installer was built,
    35  	// which even on Linux may not be the same as the architecture of the
    36  	// installer binary itself.
    37  	// Set in hack/build.sh.
    38  	defaultArch = "amd64"
    39  
    40  	// releaseArchitecture is the architecture of the release payload: multi, amd64, arm64, ppc64le, or s390x.
    41  	// we don't know the releaseArchitecure by default "".
    42  	releaseArchitecture = ""
    43  
    44  	// defaultReleaseInfoPadded may be replaced in the binary with Release Metadata: Version that overrides defaultVersion as
    45  	// a null-terminated string within the allowed character length. This allows a distributor to override the payload
    46  	// location without having to rebuild the source.
    47  	defaultVersionPadded = "\x00_RELEASE_VERSION_LOCATION_\x00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00"
    48  	defaultVersionPrefix = "\x00_RELEASE_VERSION_LOCATION_\x00"
    49  	defaultVersionLength = len(defaultVersionPadded)
    50  
    51  	// releaseArchitecturesPadded may be replaced in the binary with Release Image Architecture(s): RELEASE_ARCHITECTURE that overrides releaseArchitecture as
    52  	// a null-terminated string within the allowed character length. This allows a distributor to override the payload
    53  	// location without having to rebuild the source.
    54  	releaseArchitecturesPadded = "\x00_RELEASE_ARCHITECTURE_LOCATION_\x00XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00"
    55  	releaseArchitecturesPrefix = "\x00_RELEASE_ARCHITECTURE_LOCATION_\x00"
    56  	releaseArchitecturesLength = len(releaseArchitecturesPadded)
    57  )
    58  
    59  // String returns the human-friendly representation of the version.
    60  func String() (string, error) {
    61  	version, err := Version()
    62  	return fmt.Sprintf("OpenShift Installer %s", version), err
    63  }
    64  
    65  // Version returns the installer/release version.
    66  func Version() (string, error) {
    67  	if strings.HasPrefix(defaultVersionPadded, defaultVersionPrefix) {
    68  		return Raw, nil
    69  	}
    70  	nullTerminator := strings.IndexByte(defaultVersionPadded, '\x00')
    71  	if nullTerminator == -1 {
    72  		// the binary has been altered, but we didn't find a null terminator within the release name constant which is an error
    73  		return Raw, fmt.Errorf("release name location was replaced but without a null terminator before %d bytes", defaultVersionLength)
    74  	}
    75  	if nullTerminator > len(defaultVersionPadded) {
    76  		// the binary has been altered, but the null terminator is *longer* than the constant encoded in the binary
    77  		return Raw, fmt.Errorf("release name location contains no null-terminator and constant is corrupted")
    78  	}
    79  	releaseName := defaultVersionPadded[:nullTerminator]
    80  	if len(releaseName) == 0 {
    81  		// the binary has been altered, but the replaced release name is empty which is incorrect
    82  		// the oc binary will not be pinned to Release Metadata:Version
    83  		return Raw, fmt.Errorf("release name was incorrectly replaced during extract")
    84  	}
    85  	return releaseName, nil
    86  }
    87  
    88  // ReleaseArchitecture returns the release image cpu architecture version.
    89  func ReleaseArchitecture() (string, error) {
    90  	ri, okRI := os.LookupEnv("OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE")
    91  	if okRI {
    92  		logrus.Warnf("Found override for release image (%s). Release Image Architecture is unknown", ri)
    93  		return "unknown", nil
    94  	}
    95  	if strings.HasPrefix(releaseArchitecturesPadded, releaseArchitecturesPrefix) {
    96  		logrus.Warn("Release Image Architecture not detected. Release Image Architecture is unknown")
    97  		return "unknown", nil
    98  	}
    99  	nullTerminator := strings.IndexByte(releaseArchitecturesPadded, '\x00')
   100  	if nullTerminator == -1 {
   101  		// the binary has been altered, but we didn't find a null terminator within the release architecture constant which is an error
   102  		return Raw, fmt.Errorf("release architecture location was replaced but without a null terminator before %d bytes", releaseArchitecturesLength)
   103  	}
   104  	if nullTerminator > len(releaseArchitecturesPadded) {
   105  		// the binary has been altered, but the null terminator is *longer* than the constant encoded in the binary
   106  		return Raw, fmt.Errorf("release architecture location contains no null-terminator and constant is corrupted")
   107  	}
   108  	releaseArchitecture = releaseArchitecturesPadded[:nullTerminator]
   109  	if len(releaseArchitecture) == 0 {
   110  		// the binary has been altered, but the replaced release architecture is empty which is incorrect
   111  		return Raw, fmt.Errorf("release architecture was incorrectly replaced during extract")
   112  	}
   113  	return cleanArch(releaseArchitecture), nil
   114  }
   115  
   116  // cleanArch oc will embed linux/<arch> or multi (linux/<arch>) we want to clean this up so validation can more cleanly use this method.
   117  // multi (linux/amd64) -> multi
   118  // linux/amd64 -> amd64
   119  // linux/<arch> -> <arch>.
   120  func cleanArch(releaseArchitecture string) string {
   121  	if strings.HasPrefix(releaseArchitecture, "multi") {
   122  		return "multi"
   123  	}
   124  	// remove 'linux/', we just want <arch>
   125  	return strings.ReplaceAll(releaseArchitecture, "linux/", "")
   126  }
   127  
   128  // DefaultArch returns the default release architecture
   129  func DefaultArch() types.Architecture {
   130  	return types.Architecture(defaultArch)
   131  }