github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/operator/nodeagent/dep/nginx/dep.go (about)

     1  package nginx
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"github.com/caos/orbos/internal/operator/common"
    14  	"github.com/caos/orbos/internal/operator/nodeagent"
    15  	"github.com/caos/orbos/internal/operator/nodeagent/dep"
    16  	"github.com/caos/orbos/internal/operator/nodeagent/dep/middleware"
    17  	"github.com/caos/orbos/internal/operator/nodeagent/dep/selinux"
    18  	"github.com/caos/orbos/mntr"
    19  )
    20  
    21  const LineAddedComment = "# The following line was added by CAOS node agent"
    22  const CleanupLine = "# Line added by CAOS node agent"
    23  
    24  type Installer interface {
    25  	isNgninx()
    26  	nodeagent.Installer
    27  }
    28  type nginxDep struct {
    29  	manager    *dep.PackageManager
    30  	systemd    *dep.SystemD
    31  	monitor    mntr.Monitor
    32  	os         dep.OperatingSystem
    33  	normalizer *regexp.Regexp
    34  }
    35  
    36  func New(monitor mntr.Monitor, manager *dep.PackageManager, systemd *dep.SystemD, os dep.OperatingSystem) Installer {
    37  	return &nginxDep{manager, systemd, monitor, os, regexp.MustCompile(`\d+\.\d+\.\d+`)}
    38  }
    39  
    40  func (nginxDep) isNgninx() {}
    41  
    42  func (nginxDep) Is(other nodeagent.Installer) bool {
    43  	_, ok := middleware.Unwrap(other).(Installer)
    44  	return ok
    45  }
    46  
    47  func (nginxDep) String() string { return "NGINX" }
    48  
    49  func (*nginxDep) Equals(other nodeagent.Installer) bool {
    50  	_, ok := other.(*nginxDep)
    51  	return ok
    52  }
    53  
    54  func (s *nginxDep) InstalledFilter() []string {
    55  	return []string{"nginx"}
    56  }
    57  
    58  func (s *nginxDep) Current() (pkg common.Package, err error) {
    59  	if !s.systemd.Active("nginx") {
    60  		return pkg, err
    61  	}
    62  
    63  	installed := s.manager.CurrentVersions("nginx")
    64  	if len(installed) == 0 {
    65  		return pkg, nil
    66  	}
    67  	pkg.Version = "v" + s.normalizer.FindString(installed[0].Version)
    68  
    69  	config, err := ioutil.ReadFile("/etc/nginx/nginx.conf")
    70  	if err != nil {
    71  		if os.IsNotExist(err) {
    72  			return pkg, nil
    73  		}
    74  		return pkg, err
    75  	}
    76  
    77  	pkg.Config = map[string]string{
    78  		"nginx.conf": string(config),
    79  	}
    80  
    81  	unitPath, err := s.systemd.UnitPath("nginx")
    82  	if err != nil {
    83  		return pkg, err
    84  	}
    85  
    86  	systemdUnit, err := ioutil.ReadFile(unitPath)
    87  	if err != nil {
    88  		return pkg, err
    89  	}
    90  
    91  	CurrentSystemdEntries(bytes.NewReader(systemdUnit), &pkg)
    92  
    93  	return pkg, nil
    94  }
    95  
    96  func (s *nginxDep) Ensure(remove common.Package, ensure common.Package, leaveOSRepositories bool) error {
    97  
    98  	if err := selinux.EnsurePermissive(s.monitor, s.os, remove); err != nil {
    99  		return err
   100  	}
   101  
   102  	ensureCfg, ok := ensure.Config["nginx.conf"]
   103  	if !ok {
   104  		s.systemd.Disable("nginx")
   105  		os.Remove("/etc/nginx/nginx.conf")
   106  		return nil
   107  	}
   108  
   109  	// TODO: Why does this not prevent updating nginx?
   110  	if _, ok := remove.Config["nginx.conf"]; !ok {
   111  
   112  		swmonitor := s.monitor.WithField("software", "NGINX")
   113  
   114  		if !leaveOSRepositories {
   115  			repoURL := "http://nginx.org/packages/centos/$releasever/$basearch/"
   116  			if err := ioutil.WriteFile("/etc/yum.repos.d/nginx.repo", []byte(fmt.Sprintf(`[nginx-stable]
   117  name=nginx stable repo
   118  baseurl=%s
   119  gpgcheck=1
   120  enabled=1
   121  gpgkey=https://nginx.org/keys/nginx_signing.key
   122  module_hotfixes=true`, repoURL)), 0644); err != nil {
   123  				return err
   124  			}
   125  
   126  			swmonitor.WithField("url", repoURL).Info("repo added")
   127  		}
   128  
   129  		if err := s.manager.Install(&dep.Software{
   130  			Package: "nginx",
   131  			Version: strings.TrimLeft(ensure.Version, "v"),
   132  		}); err != nil {
   133  			return fmt.Errorf("installing software failed: %w", err)
   134  		}
   135  	}
   136  
   137  	if err := os.MkdirAll("/etc/nginx", 0700); err != nil {
   138  		return err
   139  	}
   140  
   141  	if err := ioutil.WriteFile("/etc/nginx/nginx.conf", []byte(ensureCfg), 0600); err != nil {
   142  		return err
   143  	}
   144  
   145  	unitPath, err := s.systemd.UnitPath("nginx")
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	if err := UpdateSystemdUnitFile(unitPath, ensure.Config); err != nil {
   151  		return err
   152  	}
   153  
   154  	if err := s.systemd.Enable("nginx"); err != nil {
   155  		return err
   156  	}
   157  
   158  	return s.systemd.Start("nginx")
   159  }
   160  
   161  func CurrentSystemdEntries(r io.Reader, p *common.Package) {
   162  
   163  	sectionRegexp := regexp.MustCompile("^\\[[a-zA-Z]+]$")
   164  	scanner := bufio.NewScanner(r)
   165  	var addNextLine bool
   166  	var currentSection string
   167  	for scanner.Scan() {
   168  		line := scanner.Text()
   169  		section := sectionRegexp.FindString(line)
   170  		if len(section) >= 1 {
   171  			currentSection = section
   172  		}
   173  
   174  		lineparts := strings.Split(line, "=")
   175  
   176  		if strings.Contains(line, CleanupLine) {
   177  			p.Config["ensuresystemdconf"] = "yes"
   178  		}
   179  
   180  		if addNextLine {
   181  			p.Config[fmt.Sprintf("Systemd%s%s", currentSection, lineparts[0])] = lineparts[1]
   182  			addNextLine = false
   183  		}
   184  		addNextLine = strings.Contains(line, LineAddedComment)
   185  	}
   186  }
   187  
   188  func UpdateSystemdUnitFile(path string, cfg map[string]string) error {
   189  
   190  	removeContaining := []string{CleanupLine, LineAddedComment}
   191  	sectionRegexpStr := "\\[[a-zA-Z]+\\]"
   192  	keyPartsRegexp := regexp.MustCompile(fmt.Sprintf("^Systemd(%s)([a-zA-Z]+)$", sectionRegexpStr))
   193  	sectionRegexpLine := regexp.MustCompile(fmt.Sprintf("^%s$", sectionRegexpStr))
   194  
   195  	for k := range cfg {
   196  		parts := keyPartsRegexp.FindStringSubmatch(k)
   197  		if len(parts) == 3 {
   198  			removeContaining = append(removeContaining, parts[2]+"=")
   199  		}
   200  	}
   201  
   202  	return dep.ManipulateFile(path, removeContaining, nil, func(line string) *string {
   203  
   204  		if !sectionRegexpLine.MatchString(line) {
   205  			return strPtr(line)
   206  		}
   207  
   208  		addLines := []string{line}
   209  
   210  		for k, v := range cfg {
   211  			parts := keyPartsRegexp.FindStringSubmatch(k)
   212  			if len(parts) == 3 {
   213  				if parts[1] == line {
   214  					addLines = append(addLines, LineAddedComment, fmt.Sprintf("%s=%s", parts[2], v))
   215  				}
   216  			}
   217  		}
   218  
   219  		return strPtr(strings.Join(addLines, "\n"))
   220  	})
   221  }
   222  
   223  func strPtr(str string) *string { return &str }