github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/cloudconfig/cloudinit/cloudinit_centos.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Copyright 2015 Cloudbase Solutions SRL
     3  // Licensed under the AGPLv3, see LICENCE file for details.
     4  
     5  package cloudinit
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/juju/packaging/v3"
    12  	"github.com/juju/packaging/v3/config"
    13  	"gopkg.in/yaml.v2"
    14  
    15  	corenetwork "github.com/juju/juju/core/network"
    16  	jujupackaging "github.com/juju/juju/packaging"
    17  )
    18  
    19  // PackageHelper is the interface for configuring specific parameter of the package manager
    20  type packageHelper interface {
    21  	// addPackageProxyCmd is a helper method which returns the corresponding runcmd
    22  	// to apply the package proxy settings.
    23  	addPackageProxyCmd(url string) string
    24  
    25  	//Returns the required packages, depending on the OS
    26  	getRequiredPackages() []string
    27  }
    28  
    29  // Implementation of PackageHelper for CentOS
    30  type centOSHelper struct {
    31  }
    32  
    33  // Returns the list of required packages in CentOS
    34  func (helper centOSHelper) getRequiredPackages() []string {
    35  	return []string{
    36  		"curl",
    37  		"nmap-ncat",
    38  		"tmux",
    39  		"tar",
    40  	}
    41  }
    42  
    43  // addPackageProxyCmd is a helper method which returns the corresponding runcmd
    44  // to apply the package proxy settings for CentOS
    45  func (helper centOSHelper) addPackageProxyCmd(url string) string {
    46  	return fmt.Sprintf("/bin/echo 'proxy=%s' >> /etc/yum.conf", url)
    47  }
    48  
    49  // centOSCloudConfig is the cloudconfig type specific to CentOS machines.
    50  // It simply contains a cloudConfig and adds the package management related
    51  // methods for CentOS, which are mostly modeled as runcmds.
    52  // It implements the cloudinit.Config interface.
    53  type centOSCloudConfig struct {
    54  	*cloudConfig
    55  	helper packageHelper
    56  }
    57  
    58  // SetPackageProxy is defined on the PackageProxyConfig interface.
    59  func (cfg *centOSCloudConfig) SetPackageProxy(url string) {
    60  	cfg.SetAttr("package_proxy", url)
    61  }
    62  
    63  // UnsetPackageProxy is defined on the PackageProxyConfig interface.
    64  func (cfg *centOSCloudConfig) UnsetPackageProxy() {
    65  	cfg.UnsetAttr("package_proxy")
    66  }
    67  
    68  // PackageProxy is defined on the PackageProxyConfig interface.
    69  func (cfg *centOSCloudConfig) PackageProxy() string {
    70  	proxy, _ := cfg.attrs["package_proxy"].(string)
    71  	return proxy
    72  }
    73  
    74  // SetPackageMirror is defined on the PackageMirrorConfig interface.
    75  func (cfg *centOSCloudConfig) SetPackageMirror(url string) {
    76  	cfg.SetAttr("package_mirror", url)
    77  }
    78  
    79  // addPackageMirrorCmd is a helper function that returns the corresponding runcmds
    80  // to apply the package mirror settings on a CentOS machine.
    81  func addPackageMirrorCmd(cfg CloudConfig, url string) string {
    82  	return fmt.Sprintf(config.ReplaceCentOSMirror, url)
    83  }
    84  
    85  // UnsetPackageMirror is defined on the PackageMirrorConfig interface.
    86  func (cfg *centOSCloudConfig) UnsetPackageMirror() {
    87  	cfg.UnsetAttr("package_mirror")
    88  }
    89  
    90  // PackageMirror is defined on the PackageMirrorConfig interface.
    91  func (cfg *centOSCloudConfig) PackageMirror() string {
    92  	mirror, _ := cfg.attrs["package_mirror"].(string)
    93  	return mirror
    94  }
    95  
    96  // AddPackageSource is defined on the PackageSourcesConfig interface.
    97  func (cfg *centOSCloudConfig) AddPackageSource(src packaging.PackageSource) {
    98  	cfg.attrs["package_sources"] = append(cfg.PackageSources(), src)
    99  }
   100  
   101  // PackageSources is defined on the PackageSourcesConfig interface.
   102  func (cfg *centOSCloudConfig) PackageSources() []packaging.PackageSource {
   103  	sources, _ := cfg.attrs["package_sources"].([]packaging.PackageSource)
   104  	return sources
   105  }
   106  
   107  // AddPackagePreferences is defined on the PackageSourcesConfig interface.
   108  func (cfg *centOSCloudConfig) AddPackagePreferences(prefs packaging.PackagePreferences) {
   109  	// TODO (aznashwan): research a way of using yum-priorities in the
   110  	// context of a single package and implement the appropriate runcmds.
   111  }
   112  
   113  // PackagePreferences is defined on the PackageSourcesConfig interface.
   114  func (cfg *centOSCloudConfig) PackagePreferences() []packaging.PackagePreferences {
   115  	// TODO (aznashwan): add this when priorities in yum make sense.
   116  	return []packaging.PackagePreferences{}
   117  }
   118  
   119  // Render is defined on the the Renderer interface.
   120  func (cfg *centOSCloudConfig) RenderYAML() ([]byte, error) {
   121  	// Save the fields that we will modify
   122  	var oldruncmds []string
   123  	oldruncmds = copyStringSlice(cfg.RunCmds())
   124  
   125  	// check for package proxy setting and add commands:
   126  	var proxy string
   127  	if proxy = cfg.PackageProxy(); proxy != "" {
   128  		cfg.AddRunCmd(cfg.helper.addPackageProxyCmd(proxy))
   129  		cfg.UnsetPackageProxy()
   130  	}
   131  
   132  	// check for package mirror settings and add commands:
   133  	var mirror string
   134  	if mirror = cfg.PackageMirror(); mirror != "" {
   135  		cfg.AddRunCmd(addPackageMirrorCmd(cfg, mirror))
   136  		cfg.UnsetPackageMirror()
   137  	}
   138  
   139  	// add appropriate commands for package sources configuration:
   140  	srcs := cfg.PackageSources()
   141  	for _, src := range srcs {
   142  		cfg.AddScripts(addPackageSourceCmds(cfg, src)...)
   143  	}
   144  	cfg.UnsetAttr("package_sources")
   145  
   146  	data, err := yaml.Marshal(cfg.attrs)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	// Restore the modified fields
   152  	cfg.SetPackageProxy(proxy)
   153  	cfg.SetPackageMirror(mirror)
   154  	cfg.SetAttr("package_sources", srcs)
   155  	if oldruncmds != nil {
   156  		cfg.SetAttr("runcmd", oldruncmds)
   157  	} else {
   158  		cfg.UnsetAttr("runcmd")
   159  	}
   160  
   161  	return append([]byte("#cloud-config\n"), data...), nil
   162  }
   163  
   164  func (cfg *centOSCloudConfig) RenderScript() (string, error) {
   165  	return renderScriptCommon(cfg)
   166  }
   167  
   168  func (cfg *centOSCloudConfig) getCommandsForAddingPackages() ([]string, error) {
   169  	var cmds []string
   170  
   171  	if newMirror := cfg.PackageMirror(); newMirror != "" {
   172  		cmds = append(cmds, LogProgressCmd("Changing package mirror does not yet work on CentOS"))
   173  		// TODO(bogdanteleaga, aznashwan): This should work after a further PR
   174  		// where we add more mirrror options values to environs.Config
   175  		cmds = append(cmds, addPackageMirrorCmd(cfg, newMirror))
   176  	}
   177  
   178  	pkgCmder := cfg.paccmder[jujupackaging.YumPackageManager]
   179  	for _, src := range cfg.PackageSources() {
   180  		// TODO(bogdanteleaga. aznashwan): Keys are usually offered by repositories, and you need to
   181  		// accept them. Check how this can be done non interactively.
   182  		cmds = append(cmds, LogProgressCmd("Adding yum repository: %s", src.URL))
   183  		cmds = append(cmds, pkgCmder.AddRepositoryCmd(src.URL))
   184  	}
   185  
   186  	// TODO(bogdanteleaga. aznashwan): Research what else needs to be done here
   187  
   188  	// Define the "package_get_loop" function
   189  	cmds = append(cmds, config.PackageManagerLoopFunction)
   190  
   191  	if cfg.SystemUpdate() {
   192  		cmds = append(cmds, LogProgressCmd("Running yum update"))
   193  		cmds = append(cmds, "package_manager_loop "+pkgCmder.UpdateCmd())
   194  	}
   195  	if cfg.SystemUpgrade() {
   196  		cmds = append(cmds, LogProgressCmd("Running yum upgrade"))
   197  		cmds = append(cmds, "package_manager_loop "+pkgCmder.UpgradeCmd())
   198  	}
   199  
   200  	pkgs := cfg.Packages()
   201  	if len(pkgs) > 0 {
   202  		cmds = append([]string{LogProgressCmd(fmt.Sprintf("Installing %s", strings.Join(pkgs, ", ")))}, cmds...)
   203  	}
   204  	for _, pkg := range pkgs {
   205  		cmds = append(cmds, "package_manager_loop "+pkgCmder.InstallCmd(pkg))
   206  	}
   207  	return cmds, nil
   208  }
   209  
   210  // AddPackageCommands is defined on the AdvancedPackagingConfig interface.
   211  func (cfg *centOSCloudConfig) AddPackageCommands(
   212  	proxyCfg PackageManagerProxyConfig,
   213  	addUpdateScripts bool,
   214  	addUpgradeScripts bool,
   215  ) error {
   216  	return addPackageCommandsCommon(
   217  		cfg,
   218  		proxyCfg,
   219  		addUpdateScripts,
   220  		addUpgradeScripts,
   221  	)
   222  }
   223  
   224  // addRequiredPackages is defined on the AdvancedPackagingConfig interface.
   225  func (cfg *centOSCloudConfig) addRequiredPackages() {
   226  	packages := cfg.helper.getRequiredPackages()
   227  	for _, pack := range packages {
   228  		cfg.AddPackage(pack)
   229  	}
   230  }
   231  
   232  // TODO(bogdanteleaga, aznashwan): On ubuntu when we render the conf as yaml we
   233  // have apt_proxy and when we render it as bash we use the equivalent of this.
   234  // However on centOS even when rendering the YAML we use a helper function
   235  // addPackageProxyCmds. Research if calling the same is fine.
   236  func (cfg *centOSCloudConfig) updateProxySettings(PackageManagerProxyConfig) error {
   237  	return nil
   238  }
   239  
   240  // AddNetworkConfig is defined on the NetworkingConfig interface.
   241  // TODO(wpk) This has to be implemented for CentOS on VSphere to work properly!
   242  func (cfg *centOSCloudConfig) AddNetworkConfig(interfaces corenetwork.InterfaceInfos) error {
   243  	return nil
   244  }