| // Copyright 2020 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package config |
| |
| import ( |
| "context" |
| "encoding/base64" |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| "time" |
| |
| "github.com/GoogleCloudPlatform/osconfig/agentconfig" |
| "github.com/GoogleCloudPlatform/osconfig/clog" |
| "github.com/GoogleCloudPlatform/osconfig/packages" |
| "github.com/GoogleCloudPlatform/osconfig/util" |
| |
| "cloud.google.com/go/osconfig/agentendpoint/apiv1/agentendpointpb" |
| "google.golang.org/protobuf/proto" |
| ) |
| |
| var ( |
| packageInfoCacheFile = filepath.Join(agentconfig.CacheDir(), "config_package_info.cache") |
| // Clear out the entry if the last lookup is > 7 days ago. |
| packageInfoCacheTimeout = -168 * time.Hour |
| packageInfoCacheStore packageInfoCache |
| ) |
| |
| type packageResouce struct { |
| *agentendpointpb.OSPolicy_Resource_PackageResource |
| |
| managedPackage ManagedPackage |
| } |
| |
| // AptPackage describes an apt package resource. |
| type AptPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_APT |
| DesiredState agentendpointpb.OSPolicy_Resource_PackageResource_DesiredState |
| } |
| |
| // DebPackage describes a deb package resource. |
| type DebPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_Deb |
| name, localPath string |
| } |
| |
| // GooGetPackage describes a googet package resource. |
| type GooGetPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_GooGet |
| DesiredState agentendpointpb.OSPolicy_Resource_PackageResource_DesiredState |
| } |
| |
| // MSIPackage describes an msi package resource. |
| type MSIPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_MSI |
| productName, productCode, localPath string |
| } |
| |
| // YumPackage describes a yum package resource. |
| type YumPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_YUM |
| DesiredState agentendpointpb.OSPolicy_Resource_PackageResource_DesiredState |
| } |
| |
| // ZypperPackage describes a zypper package resource. |
| type ZypperPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_Zypper |
| DesiredState agentendpointpb.OSPolicy_Resource_PackageResource_DesiredState |
| } |
| |
| // RPMPackage describes an rpm package resource. |
| type RPMPackage struct { |
| PackageResource *agentendpointpb.OSPolicy_Resource_PackageResource_RPM |
| name, localPath string |
| } |
| |
| // ManagedPackage is the package that this PackageResource manages. |
| type ManagedPackage struct { |
| Apt *AptPackage |
| Deb *DebPackage |
| GooGet *GooGetPackage |
| MSI *MSIPackage |
| Yum *YumPackage |
| Zypper *ZypperPackage |
| RPM *RPMPackage |
| |
| tempDir string |
| } |
| |
| func (p *packageResouce) validateFile(file *agentendpointpb.OSPolicy_Resource_File) error { |
| if file.GetLocalPath() != "" { |
| if !util.Exists(file.GetLocalPath()) { |
| return fmt.Errorf("%q does not exist", file.GetLocalPath()) |
| } |
| } |
| return nil |
| } |
| |
| type packageInfoCache map[string]packageInfo |
| |
| type packageInfo struct { |
| PkgInfo *packages.PkgInfo |
| LastLookup time.Time |
| } |
| |
| func getPackageInfoCacheKey(pkgFile *agentendpointpb.OSPolicy_Resource_File) (string, error) { |
| // We use the base64 encoded binary proto as the key. |
| raw, err := proto.Marshal(pkgFile) |
| if err != nil { |
| return "", err |
| } |
| return base64.RawStdEncoding.EncodeToString(raw), nil |
| } |
| |
| func loadPackageInfoCache(ctx context.Context) { |
| if packageInfoCacheStore != nil { |
| return |
| } |
| data, err := ioutil.ReadFile(packageInfoCacheFile) |
| if err != nil { |
| // Just ignore the error and return early |
| // The error mode here is to just always redownload the file. |
| clog.Debugf(ctx, "Error reading the package info cache: %v", err) |
| packageInfoCacheStore = packageInfoCache{} |
| return |
| } |
| var cache packageInfoCache |
| if err := json.Unmarshal(data, &cache); err != nil { |
| clog.Debugf(ctx, "Error unmarshaling the package info cache: %v", err) |
| packageInfoCacheStore = packageInfoCache{} |
| return |
| } |
| packageInfoCacheStore = cache |
| } |
| |
| func getPackageInfoFromCache(ctx context.Context, pkgFile *agentendpointpb.OSPolicy_Resource_File) *packages.PkgInfo { |
| loadPackageInfoCache(ctx) |
| key, err := getPackageInfoCacheKey(pkgFile) |
| if err != nil { |
| // Just ignore the error and return early |
| // The error mode here is just always redownload the file. |
| clog.Debugf(ctx, "Error creating the package info cache key: %v", err) |
| return nil |
| } |
| packageInfo, ok := packageInfoCacheStore[key] |
| if !ok { |
| return nil |
| } |
| return packageInfo.PkgInfo |
| } |
| |
| func updatePackageInfoCache(ctx context.Context, info *packages.PkgInfo, pkgFile *agentendpointpb.OSPolicy_Resource_File) { |
| loadPackageInfoCache(ctx) |
| for k, v := range packageInfoCacheStore { |
| if time.Now().Add(packageInfoCacheTimeout).After(v.LastLookup) { |
| delete(packageInfoCacheStore, k) |
| } |
| } |
| key, err := getPackageInfoCacheKey(pkgFile) |
| if err != nil { |
| // Just ignore the error and return early |
| // The error mode here is to just always redownload the file. |
| clog.Warningf(ctx, "Error creating the package info cache: %v", err) |
| return |
| } |
| packageInfoCacheStore[key] = packageInfo{PkgInfo: info, LastLookup: time.Now()} |
| } |
| |
| func savePackageInfoCache(ctx context.Context) error { |
| if packageInfoCacheStore == nil { |
| return nil |
| } |
| data, err := json.Marshal(packageInfoCacheStore) |
| if err != nil { |
| return err |
| } |
| packageInfoCacheStore = nil |
| return ioutil.WriteFile(packageInfoCacheFile, data, 0644) |
| } |
| |
| func (p *packageResouce) validate(ctx context.Context) (*ManagedResources, error) { |
| switch p.GetSystemPackage().(type) { |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Apt: |
| pr := p.GetApt() |
| if !packages.AptExists { |
| return nil, fmt.Errorf("cannot manage Apt package %q because apt-get does not exist on the system", pr.GetName()) |
| } |
| |
| p.managedPackage.Apt = &AptPackage{DesiredState: p.GetDesiredState(), PackageResource: pr} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Deb_: |
| pr := p.GetDeb() |
| if !packages.DpkgExists { |
| return nil, fmt.Errorf("cannot manage Deb package because dpkg does not exist on the system") |
| } |
| if p.GetDesiredState() != agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED { |
| return nil, fmt.Errorf("desired state of %q not applicable for deb package", p.GetDesiredState()) |
| } |
| if err := p.validateFile(pr.GetSource()); err != nil { |
| return nil, err |
| } |
| var localPath string |
| var err error |
| source := p.GetDeb().GetSource() |
| info := getPackageInfoFromCache(ctx, source) |
| if info == nil { |
| localPath, err = p.download(ctx, "pkg.deb", source) |
| if err != nil { |
| return nil, err |
| } |
| info, err = packages.DebPkgInfo(ctx, localPath) |
| if err != nil { |
| return nil, err |
| } |
| } |
| // Always update the cache to update the timestamps. |
| updatePackageInfoCache(ctx, info, source) |
| |
| p.managedPackage.Deb = &DebPackage{PackageResource: pr, localPath: localPath, name: info.Name} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Googet: |
| pr := p.GetGooget() |
| if !packages.GooGetExists { |
| return nil, fmt.Errorf("cannot manage GooGet package %q because googet does not exist on the system", pr.GetName()) |
| } |
| |
| p.managedPackage.GooGet = &GooGetPackage{DesiredState: p.GetDesiredState(), PackageResource: pr} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Msi: |
| pr := p.GetMsi() |
| if !packages.MSIExists { |
| return nil, fmt.Errorf("cannot manage MSI package because msiexec does not exist on the system") |
| } |
| if p.GetDesiredState() != agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED { |
| return nil, fmt.Errorf("desired state of %q not applicable for MSI package", p.GetDesiredState()) |
| } |
| if err := p.validateFile(pr.GetSource()); err != nil { |
| return nil, err |
| } |
| var localPath string |
| var err error |
| source := p.GetMsi().GetSource() |
| info := getPackageInfoFromCache(ctx, source) |
| if info == nil { |
| localPath, err = p.download(ctx, "pkg.msi", source) |
| if err != nil { |
| return nil, err |
| } |
| productName, productCode, err := packages.MSIInfo(localPath) |
| if err != nil { |
| return nil, err |
| } |
| // We store productCode as version in the packageinfo struct. |
| info = &packages.PkgInfo{Name: productName, Version: productCode} |
| } |
| // Always update the cache to update the timestamps. |
| updatePackageInfoCache(ctx, info, source) |
| |
| p.managedPackage.MSI = &MSIPackage{PackageResource: pr, localPath: localPath, productName: info.Name, productCode: info.Version} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Yum: |
| pr := p.GetYum() |
| if !packages.YumExists { |
| return nil, fmt.Errorf("cannot manage Yum package %q because yum does not exist on the system", pr.GetName()) |
| } |
| |
| p.managedPackage.Yum = &YumPackage{DesiredState: p.GetDesiredState(), PackageResource: pr} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Zypper_: |
| pr := p.GetZypper() |
| if !packages.ZypperExists { |
| return nil, fmt.Errorf("cannot manage Zypper package %q because zypper does not exist on the system", pr.GetName()) |
| } |
| |
| p.managedPackage.Zypper = &ZypperPackage{DesiredState: p.GetDesiredState(), PackageResource: pr} |
| |
| case *agentendpointpb.OSPolicy_Resource_PackageResource_Rpm: |
| pr := p.GetRpm() |
| if !packages.RPMExists { |
| return nil, fmt.Errorf("cannot manage RPM package because rpm does not exist on the system") |
| } |
| if p.GetDesiredState() != agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED { |
| return nil, fmt.Errorf("desired state of %q not applicable for rpm package", p.GetDesiredState()) |
| } |
| if err := p.validateFile(pr.GetSource()); err != nil { |
| return nil, err |
| } |
| var localPath string |
| var err error |
| source := p.GetRpm().GetSource() |
| info := getPackageInfoFromCache(ctx, source) |
| if info == nil { |
| localPath, err = p.download(ctx, "pkg.rpm", source) |
| if err != nil { |
| return nil, err |
| } |
| info, err = packages.RPMPkgInfo(ctx, localPath) |
| if err != nil { |
| return nil, err |
| } |
| } |
| // Always update the cache to update the timestamps. |
| updatePackageInfoCache(ctx, info, source) |
| |
| p.managedPackage.RPM = &RPMPackage{PackageResource: pr, localPath: localPath, name: info.Name} |
| |
| default: |
| return nil, fmt.Errorf("SystemPackage field not set or references unknown package manager: %v", p.GetSystemPackage()) |
| } |
| |
| return &ManagedResources{Packages: []ManagedPackage{p.managedPackage}}, nil |
| } |
| |
| type packageCache struct { |
| cache map[string]struct{} |
| refreshed time.Time |
| } |
| |
| var ( |
| aptInstalled = &packageCache{} |
| debInstalled = &packageCache{} |
| gooInstalled = &packageCache{} |
| yumInstalled = &packageCache{} |
| zypperInstalled = &packageCache{} |
| rpmInstalled = &packageCache{} |
| ) |
| |
| func populateInstalledCache(ctx context.Context, mp ManagedPackage) error { |
| var cache *packageCache |
| var refreshFunc func(context.Context) ([]*packages.PkgInfo, error) |
| var err error |
| switch { |
| case mp.Apt != nil: |
| cache = aptInstalled |
| refreshFunc = packages.InstalledDebPackages |
| |
| case mp.Deb != nil: |
| cache = debInstalled |
| refreshFunc = packages.InstalledDebPackages |
| |
| case mp.GooGet != nil: |
| cache = gooInstalled |
| refreshFunc = packages.InstalledGooGetPackages |
| |
| case mp.MSI != nil: |
| // We just query per each MSI. |
| return nil |
| |
| // TODO: implement yum functions |
| case mp.Yum != nil: |
| cache = yumInstalled |
| refreshFunc = packages.InstalledRPMPackages |
| |
| // TODO: implement zypper functions |
| case mp.Zypper != nil: |
| cache = zypperInstalled |
| refreshFunc = packages.InstalledRPMPackages |
| |
| case mp.RPM != nil: |
| cache = rpmInstalled |
| refreshFunc = packages.InstalledRPMPackages |
| default: |
| return fmt.Errorf("unknown or unpopulated ManagedPackage package type: %+v", mp) |
| } |
| |
| // Cache already populated within the last 3 min. |
| if cache.cache != nil && cache.refreshed.After(time.Now().Add(-3*time.Minute)) { |
| return nil |
| } |
| |
| pis, err := refreshFunc(ctx) |
| if err != nil { |
| return err |
| } |
| |
| cache.cache = map[string]struct{}{} |
| for _, pkg := range pis { |
| cache.cache[pkg.Name] = struct{}{} |
| } |
| cache.refreshed = time.Now() |
| |
| return nil |
| } |
| |
| // TODO: use a persistent cache for downloaded files so we dont need to redownload them each time |
| func (p *packageResouce) download(ctx context.Context, name string, file *agentendpointpb.OSPolicy_Resource_File) (string, error) { |
| var path string |
| perms := os.FileMode(0644) |
| switch { |
| case file.GetLocalPath() != "": |
| path = file.GetLocalPath() |
| default: |
| tmpDir, err := ioutil.TempDir("", "osconfig_package_resource_") |
| if err != nil { |
| return "", fmt.Errorf("failed to create temp dir: %s", err) |
| } |
| p.managedPackage.tempDir = tmpDir |
| path = filepath.Join(p.managedPackage.tempDir, name) |
| if _, err := downloadFile(ctx, path, perms, file); err != nil { |
| return "", err |
| } |
| } |
| |
| return path, nil |
| } |
| |
| func (p *packageResouce) checkState(ctx context.Context) (inDesiredState bool, err error) { |
| if err := populateInstalledCache(ctx, p.managedPackage); err != nil { |
| return false, err |
| } |
| |
| var desiredState agentendpointpb.OSPolicy_Resource_PackageResource_DesiredState |
| var pkgIns bool |
| |
| switch { |
| case p.managedPackage.Apt != nil: |
| desiredState = p.managedPackage.Apt.DesiredState |
| _, pkgIns = aptInstalled.cache[p.managedPackage.Apt.PackageResource.GetName()] |
| |
| case p.managedPackage.Deb != nil: |
| desiredState = agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED |
| _, pkgIns = debInstalled.cache[p.managedPackage.Deb.name] |
| |
| case p.managedPackage.GooGet != nil: |
| desiredState = p.managedPackage.GooGet.DesiredState |
| _, pkgIns = gooInstalled.cache[p.managedPackage.GooGet.PackageResource.GetName()] |
| |
| case p.managedPackage.MSI != nil: |
| desiredState = agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED |
| pkgIns, err = packages.MSIInstalled(p.managedPackage.MSI.productCode) |
| if err != nil { |
| return false, err |
| } |
| |
| case p.managedPackage.Yum != nil: |
| desiredState = p.managedPackage.Yum.DesiredState |
| _, pkgIns = yumInstalled.cache[p.managedPackage.Yum.PackageResource.GetName()] |
| |
| case p.managedPackage.Zypper != nil: |
| desiredState = p.managedPackage.Zypper.DesiredState |
| _, pkgIns = zypperInstalled.cache[p.managedPackage.Zypper.PackageResource.GetName()] |
| |
| case p.managedPackage.RPM != nil: |
| desiredState = agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED |
| _, pkgIns = rpmInstalled.cache[p.managedPackage.RPM.name] |
| } |
| |
| switch desiredState { |
| case agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED: |
| if pkgIns { |
| return true, nil |
| } |
| case agentendpointpb.OSPolicy_Resource_PackageResource_REMOVED: |
| if !pkgIns { |
| return true, nil |
| } |
| default: |
| return false, fmt.Errorf("DesiredState field not set or references state: %q", desiredState) |
| } |
| |
| return false, nil |
| } |
| |
| func (p *packageResouce) enforceState(ctx context.Context) (inDesiredState bool, err error) { |
| var ( |
| installing = "installing" |
| removing = "removing" |
| |
| enforcePackage struct { |
| actionFunc func() error |
| installedCache *packageCache |
| name string |
| action string |
| packageType string |
| } |
| ) |
| |
| switch { |
| case p.managedPackage.Apt != nil: |
| enforcePackage.name = p.managedPackage.Apt.PackageResource.GetName() |
| enforcePackage.packageType = "apt" |
| enforcePackage.installedCache = aptInstalled |
| switch p.managedPackage.Apt.DesiredState { |
| case agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED: |
| enforcePackage.action, enforcePackage.actionFunc = installing, func() error { |
| if _, err := packages.AptUpdate(ctx); err != nil { |
| return err |
| } |
| return packages.InstallAptPackages(ctx, []string{enforcePackage.name}) |
| } |
| case agentendpointpb.OSPolicy_Resource_PackageResource_REMOVED: |
| enforcePackage.action, enforcePackage.actionFunc = removing, func() error { return packages.RemoveAptPackages(ctx, []string{enforcePackage.name}) } |
| } |
| |
| case p.managedPackage.Deb != nil: |
| enforcePackage.name = p.managedPackage.Deb.name |
| enforcePackage.packageType = "deb" |
| enforcePackage.installedCache = debInstalled |
| enforcePackage.action = installing |
| // Check if we have not pulled the package yet. |
| if p.managedPackage.Deb.localPath == "" { |
| localPath, err := p.download(ctx, "pkg.deb", p.GetDeb().GetSource()) |
| if err != nil { |
| return false, err |
| } |
| p.managedPackage.Deb.localPath = localPath |
| } |
| if p.GetDeb().GetPullDeps() { |
| enforcePackage.actionFunc = func() error { return packages.InstallAptPackages(ctx, []string{p.managedPackage.Deb.localPath}) } |
| } else { |
| enforcePackage.actionFunc = func() error { return packages.DpkgInstall(ctx, p.managedPackage.Deb.localPath) } |
| } |
| |
| case p.managedPackage.GooGet != nil: |
| enforcePackage.name = p.managedPackage.GooGet.PackageResource.GetName() |
| enforcePackage.packageType = "googet" |
| enforcePackage.installedCache = gooInstalled |
| switch p.managedPackage.GooGet.DesiredState { |
| case agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED: |
| enforcePackage.action, enforcePackage.actionFunc = installing, func() error { return packages.InstallGooGetPackages(ctx, []string{enforcePackage.name}) } |
| case agentendpointpb.OSPolicy_Resource_PackageResource_REMOVED: |
| enforcePackage.action, enforcePackage.actionFunc = removing, func() error { return packages.RemoveGooGetPackages(ctx, []string{enforcePackage.name}) } |
| } |
| |
| case p.managedPackage.MSI != nil: |
| enforcePackage.name = p.managedPackage.MSI.productName |
| enforcePackage.packageType = "msi" |
| enforcePackage.action = installing |
| enforcePackage.installedCache = &packageCache{} // No package cache for msi. |
| // Check if we have not pulled the package yet. |
| if p.managedPackage.MSI.localPath == "" { |
| localPath, err := p.download(ctx, "pkg.msi", p.GetMsi().GetSource()) |
| if err != nil { |
| return false, err |
| } |
| p.managedPackage.MSI.localPath = localPath |
| } |
| enforcePackage.actionFunc = func() error { |
| return packages.InstallMSIPackage(ctx, p.managedPackage.MSI.localPath, p.managedPackage.MSI.PackageResource.GetProperties()) |
| } |
| |
| case p.managedPackage.Yum != nil: |
| enforcePackage.name = p.managedPackage.Yum.PackageResource.GetName() |
| enforcePackage.packageType = "yum" |
| enforcePackage.installedCache = yumInstalled |
| switch p.managedPackage.Yum.DesiredState { |
| case agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED: |
| enforcePackage.action, enforcePackage.actionFunc = installing, func() error { return packages.InstallYumPackages(ctx, []string{enforcePackage.name}) } |
| case agentendpointpb.OSPolicy_Resource_PackageResource_REMOVED: |
| enforcePackage.action, enforcePackage.actionFunc = removing, func() error { return packages.RemoveYumPackages(ctx, []string{enforcePackage.name}) } |
| } |
| |
| case p.managedPackage.Zypper != nil: |
| enforcePackage.name = p.managedPackage.Zypper.PackageResource.GetName() |
| enforcePackage.packageType = "zypper" |
| enforcePackage.installedCache = zypperInstalled |
| switch p.managedPackage.Zypper.DesiredState { |
| case agentendpointpb.OSPolicy_Resource_PackageResource_INSTALLED: |
| enforcePackage.action, enforcePackage.actionFunc = installing, func() error { return packages.InstallZypperPackages(ctx, []string{enforcePackage.name}) } |
| case agentendpointpb.OSPolicy_Resource_PackageResource_REMOVED: |
| enforcePackage.action, enforcePackage.actionFunc = removing, func() error { return packages.RemoveZypperPackages(ctx, []string{enforcePackage.name}) } |
| } |
| |
| case p.managedPackage.RPM != nil: |
| enforcePackage.name = p.managedPackage.RPM.name |
| enforcePackage.packageType = "rpm" |
| enforcePackage.installedCache = rpmInstalled |
| enforcePackage.action = installing |
| // Check if we have not pulled the package yet. |
| if p.managedPackage.RPM.localPath == "" { |
| localPath, err := p.download(ctx, "pkg.rpm", p.GetRpm().GetSource()) |
| if err != nil { |
| return false, err |
| } |
| p.managedPackage.RPM.localPath = localPath |
| } |
| if p.GetRpm().GetPullDeps() { |
| switch { |
| case packages.YumExists: |
| enforcePackage.actionFunc = func() error { return packages.InstallYumPackages(ctx, []string{p.managedPackage.RPM.localPath}) } |
| case packages.ZypperExists: |
| enforcePackage.actionFunc = func() error { return packages.InstallZypperPackages(ctx, []string{p.managedPackage.RPM.localPath}) } |
| default: |
| return false, fmt.Errorf("cannot install rpm %q with 'PullDeps' option as neither yum or zypper exist on system", enforcePackage.name) |
| } |
| } else { |
| enforcePackage.actionFunc = func() error { return packages.RPMInstall(ctx, p.managedPackage.RPM.localPath) } |
| } |
| } |
| |
| clog.Infof(ctx, "%s %s package %q", strings.Title(enforcePackage.action), enforcePackage.packageType, enforcePackage.name) |
| // Reset the cache as we are taking action on. |
| enforcePackage.installedCache.cache = nil |
| if err := enforcePackage.actionFunc(); err != nil { |
| return false, fmt.Errorf("error %s %s package %q", enforcePackage.action, enforcePackage.packageType, enforcePackage.name) |
| } |
| |
| return true, nil |
| } |
| |
| func (p *packageResouce) populateOutput(rCompliance *agentendpointpb.OSPolicyResourceCompliance) {} |
| |
| func (p *packageResouce) cleanup(ctx context.Context) error { |
| // Save cache and clear the variable. |
| if err := savePackageInfoCache(ctx); err != nil { |
| clog.Warningf(ctx, "Error saving the package info cache: %v", err) |
| } |
| if p.managedPackage.tempDir != "" { |
| return os.RemoveAll(p.managedPackage.tempDir) |
| } |
| return nil |
| } |