Skip to content

Commit 01a9396

Browse files
committed
PRODENG-3100 Linux native MCR install
- drop install.sh for all linux configurers - linux configurer package based installers for el, sles and ubuntu - move installer download into the configurers, and drop the download phase - configurer download code has downloads globally. - only windows configurer uses the installer downloader - drop the installer download mke phase TODO - windows installs may fail for fips version, need to test ALSO - some more linting: - interface{} -> any Signed-off-by: James Nesbitt <jnesbitt@mirantis.com>
1 parent 48df1b5 commit 01a9396

29 files changed

Lines changed: 368 additions & 324 deletions

examples/terraform/aws-simple/terraform.tfvars.template

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ aws = {
88
launchpad = {
99
drain = false
1010

11-
mcr_version = "25.0.13"
11+
mcr_version = "25.0.14.2"
1212
mke_version = "3.8.8"
1313
msr_version = "2.9.28"
1414

@@ -32,7 +32,7 @@ subnets = {
3232
"main" = {
3333
"cidr" = "172.31.0.0/17",
3434
"private" = false,
35-
"nodegroups" = ["MngrA", "WrkA", "MsrA"]
35+
"nodegroups" = ["MngrA", "Wrkubuntu", "Wrkrhel", "Wrksles"]
3636
}
3737
}
3838

@@ -46,22 +46,30 @@ nodegroups = {
4646
"role" = "manager",
4747
"user_data" = "sudo ufw allow 7946/tcp ; sudo ufw allow 10250/tcp "
4848
},
49-
"WrkA" = {
50-
"platform" = "ubuntu_22.04",
49+
"Wrkrhel" = {
50+
"platform" = "rhel-9",
5151
"count" = 1,
5252
"type" = "c6a.xlarge",
5353
"volume_size" = "100",
5454
"role" = "worker",
5555
"user_data" = "sudo ufw allow 7946/tcp ; sudo ufw allow 10250/tcp "
56-
}
57-
"MsrA" = {
56+
},
57+
"Wrkubuntu" = {
5858
"platform" = "ubuntu_22.04",
5959
"count" = 1,
6060
"type" = "c6a.xlarge",
6161
"volume_size" = "100",
62-
"role" = "msr",
62+
"role" = "worker",
6363
"user_data" = "sudo ufw allow 7946/tcp ; sudo ufw allow 10250/tcp "
64-
}
64+
},
65+
"Wrksles" = {
66+
"platform" = "sles-15",
67+
"count" = 1,
68+
"type" = "c6a.xlarge",
69+
"volume_size" = "100",
70+
"role" = "worker",
71+
"user_data" = "sudo ufw allow 7946/tcp ; sudo ufw allow 10250/tcp "
72+
},
6573
}
6674

6775
// set a windows password, if you have windows nodes

jn-todo.org

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
* JNesbitt informal todo list
2+
3+
** code-quality
4+
5+
*** TODO Move product/mke/api/host.go:Host.MCRConfigure to /pkg/configurer interfaces
6+
7+
The MCRConfigure() method likely exists on the host as a convenience, but as we have common
8+
configurer functionality and the other MCR methods are on the configurer it doesn't belong
9+
on the host.
10+
11+
Another option would be to move functionality to the /pkg/mcr package.
12+
13+
On top of this, the configurer.MCRConfigPath should perhaps be an accessor pair of content,
14+
instead of a filepath. Maybe the configurer should have methods to write/read the MCR configuration.
15+
16+
** linting
17+
18+
*** general interface{} -> any replace
19+
20+
There are a lot of `interface{}` types declared in the code base that should be changed to `any`

pkg/analytics/analytics_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"testing"
55

66
"github.com/Mirantis/launchpad/pkg/config/user"
7-
"github.com/stretchr/testify/require"
87
"github.com/segmentio/analytics-go/v3"
8+
"github.com/stretchr/testify/require"
99
)
1010

1111
type mockClient struct {

pkg/configurer/centos/centos.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package centos
22

33
import (
4-
"fmt"
5-
64
"github.com/Mirantis/launchpad/pkg/configurer/enterpriselinux"
75
"github.com/k0sproject/rig"
8-
"github.com/k0sproject/rig/os"
96
"github.com/k0sproject/rig/os/registry"
107
)
118

@@ -14,20 +11,12 @@ type Configurer struct {
1411
enterpriselinux.Configurer
1512
}
1613

17-
// InstallMKEBasePackages install all the needed base packages on the host.
18-
func (c Configurer) InstallMKEBasePackages(h os.Host) error {
19-
if err := c.InstallPackage(h, "curl", "socat", "iptables", "iputils", "gzip"); err != nil {
20-
return fmt.Errorf("failed to install base packages: %w", err)
21-
}
22-
return nil
23-
}
24-
2514
func init() {
2615
registry.RegisterOSModule(
2716
func(os rig.OSVersion) bool {
2817
return os.ID == "centos"
2918
},
30-
func() interface{} {
19+
func() any {
3120
return Configurer{}
3221
},
3322
)

pkg/configurer/enterpriselinux/el.go

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,45 @@ func (c Configurer) InstallMKEBasePackages(h os.Host) error {
2626
return nil
2727
}
2828

29+
// InstallMCR install Docker EE engine on Linux.
30+
func (c Configurer) InstallMCR(h os.Host, engineConfig commonconfig.MCRConfig) error {
31+
ver, verErr := configurer.ResolveLinux(h)
32+
if verErr != nil {
33+
return fmt.Errorf("could not discover Linux version information")
34+
}
35+
36+
if isEC2 := c.isAWSInstance(h); !isEC2 {
37+
log.Debugf("%s: confirmed that this is not an AWS instance", h)
38+
} else if c.InstallPackage(h, "rh-amazon-rhui-client") == nil {
39+
log.Infof("%s: appears to be an AWS EC2 instance, installed rh-amazon-rhui-client", h)
40+
}
41+
42+
// e.g. https://repos.mirantis.com/rhel/$releasever/$basearch/<update-channel>
43+
baseURL := fmt.Sprintf("%s/%s/%s/%s/%s", engineConfig.RepoURL, ver.ID, "$releasever", "$basearch", engineConfig.Channel)
44+
// e.g. https://repos.mirantis.com/oraclelinux/gpg
45+
gpgURL := fmt.Sprintf("%s/%s/gpg", engineConfig.RepoURL, ver.ID)
46+
elRepoFilePath := "/etc/yum.repos.d/docker-ee.repo"
47+
elRepoTemplate := `[mirantis]
48+
name=Mirantis Container Runtime
49+
baseurl=%s
50+
enabled=1
51+
gpgcheck=1
52+
gpgkey=%s
53+
`
54+
elRepo := fmt.Sprintf(elRepoTemplate, baseURL, gpgURL)
55+
56+
if err := c.WriteFile(h, elRepoFilePath, elRepo, "0600"); err != nil {
57+
return fmt.Errorf("could not write Yum repo file for MCR")
58+
}
59+
60+
if err := c.InstallPackage(h, "docker.ee"); err != nil {
61+
return fmt.Errorf("package manager could not install docker-ee")
62+
}
63+
return nil
64+
}
65+
2966
// UninstallMCR uninstalls docker-ee engine.
30-
func (c Configurer) UninstallMCR(h os.Host, _ string, engineConfig commonconfig.MCRConfig) error {
67+
func (c Configurer) UninstallMCR(h os.Host, engineConfig commonconfig.MCRConfig) error {
3168
info, getDockerError := c.GetDockerInfo(h)
3269
if engineConfig.Prune {
3370
defer c.CleanupLingeringMCR(h, info)
@@ -53,20 +90,6 @@ func (c Configurer) UninstallMCR(h os.Host, _ string, engineConfig commonconfig.
5390
return nil
5491
}
5592

56-
// InstallMCR install Docker EE engine on Linux.
57-
func (c Configurer) InstallMCR(h os.Host, scriptPath string, engineConfig commonconfig.MCRConfig) error {
58-
if isEC2 := c.isAWSInstance(h); !isEC2 {
59-
log.Debugf("%s: confirmed that this is not an AWS instance", h)
60-
} else if c.InstallPackage(h, "rh-amazon-rhui-client") == nil {
61-
log.Infof("%s: appears to be an AWS EC2 instance, installed rh-amazon-rhui-client", h)
62-
}
63-
64-
if err := c.LinuxConfigurer.InstallMCR(h, scriptPath, engineConfig); err != nil {
65-
return fmt.Errorf("failed to install MCR: %w", err)
66-
}
67-
return nil
68-
}
69-
7093
// function to check if the host is an AWS instance - https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
7194
func (c Configurer) isAWSInstance(h os.Host) bool {
7295
found, err := h.ExecOutput("curl -s -m 5 http://169.254.169.254/latest/dynamic/instance-identity/document | grep region")

pkg/configurer/enterpriselinux/rhel.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func init() {
1515
func(os rig.OSVersion) bool {
1616
return os.ID == "rhel"
1717
},
18-
func() interface{} {
18+
func() any {
1919
return Rhel{}
2020
},
2121
)

pkg/configurer/enterpriselinux/rockylinux.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func init() {
1515
func(os rig.OSVersion) bool {
1616
return os.ID == "rocky"
1717
},
18-
func() interface{} {
18+
func() any {
1919
return RockyLinux{}
2020
},
2121
)

pkg/configurer/installer.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package configurer
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
"os"
10+
"strings"
11+
12+
"github.com/Mirantis/launchpad/pkg/util/fileutil"
13+
log "github.com/sirupsen/logrus"
14+
)
15+
16+
var (
17+
downloadedInstallers = map[string]string{} // Global list of downloaded installers, to prevent repetition.
18+
ErrInstallerDownloadFailed = errors.New("could not download installer")
19+
)
20+
21+
func GetInstaller(source string) (string, error) {
22+
path, ok := downloadedInstallers[source]
23+
if ok {
24+
return path, nil
25+
}
26+
27+
if path == "" {
28+
return "", fmt.Errorf("%w; skipping failed installer download", ErrInstallerDownloadFailed)
29+
}
30+
31+
path, getErr := downloadInstaller(source)
32+
if getErr != nil {
33+
return "", fmt.Errorf("%w, installer download failed; %s", ErrInstallerDownloadFailed, getErr.Error())
34+
}
35+
downloadedInstallers[source] = path
36+
return path, nil
37+
}
38+
39+
// Run does all the prep work on the hosts in parallel.
40+
func downloadInstaller(path string) (string, error) {
41+
winScript, err := getScript(path)
42+
if err != nil {
43+
return "", fmt.Errorf("failed to get Windows installer script: %w", err)
44+
}
45+
f, err := os.CreateTemp("", "installerWindows")
46+
if err != nil {
47+
return "", fmt.Errorf("failed to create temporary file for windows installer script: %w", err)
48+
}
49+
50+
_, err = f.WriteString(winScript)
51+
if err != nil {
52+
return "", fmt.Errorf("failed to write to temporary file for windows installer script: %w", err)
53+
}
54+
55+
return f.Name(), nil
56+
}
57+
58+
func parseURL(uri string) (*url.URL, error) {
59+
if !strings.Contains(uri, "://") {
60+
return &url.URL{Path: uri, Scheme: "file"}, nil
61+
}
62+
63+
u, err := url.ParseRequestURI(uri)
64+
if err != nil {
65+
return nil, fmt.Errorf("failed to parse installer URL: %w", err)
66+
}
67+
return u, nil
68+
}
69+
70+
var errInvalidScript = fmt.Errorf("invalid container runtime install script")
71+
72+
func getScript(uri string) (string, error) {
73+
u, err := parseURL(uri)
74+
if err != nil {
75+
return "", err
76+
}
77+
78+
var data string
79+
80+
if u.Scheme == "file" {
81+
data, err = readFile(u.Path)
82+
} else {
83+
data, err = downloadFile(uri)
84+
}
85+
86+
log.Debugf("read %d bytes from %s", len(data), uri)
87+
88+
if err != nil {
89+
return "", err
90+
}
91+
92+
if len(data) < 10 {
93+
// cant fit an installer into that!
94+
return "", fmt.Errorf("%w: script is too short", errInvalidScript)
95+
}
96+
97+
if !strings.HasPrefix(data, "#") {
98+
log.Warnf("possibly invalid container runtime install script in %s", uri)
99+
}
100+
101+
return data, nil
102+
}
103+
104+
func downloadFile(url string) (string, error) {
105+
log.Infof("downloading container runtime install script from %s", url)
106+
resp, err := http.Get(url) //nolint:gosec // "G107: Url provided to HTTP request as taint input" -- user-provided URL is ok here
107+
if err != nil {
108+
return "", fmt.Errorf("failed to download container runtime install script: %w", err)
109+
}
110+
defer resp.Body.Close()
111+
112+
body, err := io.ReadAll(resp.Body)
113+
if err != nil {
114+
return "", fmt.Errorf("failed to read response body: %w", err)
115+
}
116+
return string(body), nil
117+
}
118+
119+
func readFile(path string) (string, error) {
120+
log.Infof("reading container runtime install script from %s", path)
121+
122+
data, err := fileutil.LoadExternalFile(path)
123+
return string(data), err
124+
}

0 commit comments

Comments
 (0)