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 }