policies/policies.go (200 lines of code) (raw):

// Copyright 2018 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 policies configures OS Guest Policies based on osconfig API response. package policies import ( "bytes" "context" "crypto/sha256" "hash" "io" "os" "time" "github.com/GoogleCloudPlatform/osconfig/agentconfig" "github.com/GoogleCloudPlatform/osconfig/agentendpoint" "github.com/GoogleCloudPlatform/osconfig/clog" "github.com/GoogleCloudPlatform/osconfig/packages" "github.com/GoogleCloudPlatform/osconfig/policies/recipes" "github.com/GoogleCloudPlatform/osconfig/retryutil" "github.com/GoogleCloudPlatform/osconfig/tasker" "github.com/GoogleCloudPlatform/osconfig/util" "cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb" ) func run(ctx context.Context) { var resp *agentendpointpb.EffectiveGuestPolicy client, err := agentendpoint.NewBetaClient(ctx) if err != nil { clog.Errorf(ctx, "agentendpoint.NewBetaClient Error: %v", err) } else { defer client.Close() resp, err = client.LookupEffectiveGuestPolicies(ctx) if err != nil { clog.Errorf(ctx, "Error running LookupEffectiveGuestPolicies: %v", err) } } local, err := readLocalConfig(ctx) if err != nil { clog.Errorf(ctx, "Error reading local software config: %v", err) } effective := mergeConfigs(local, resp) // We don't check the error from setConfig or installRecipes as all errors are already logged. setConfig(ctx, effective) installRecipes(ctx, effective) } // Run looks up osconfigs and applies them using tasker.Enqueue. func Run(ctx context.Context) { tasker.Enqueue(ctx, "Run GuestPolicies", func() { run(ctx) }) } func installRecipes(ctx context.Context, egp *agentendpointpb.EffectiveGuestPolicy) error { for _, recipe := range egp.GetSoftwareRecipes() { if r := recipe.GetSoftwareRecipe(); r != nil { if err := recipes.InstallRecipe(ctx, r); err != nil { clog.Errorf(ctx, "Error installing recipe: %v", err) } } } return nil } func setConfig(ctx context.Context, egp *agentendpointpb.EffectiveGuestPolicy) { var aptRepos []*agentendpointpb.AptRepository var yumRepos []*agentendpointpb.YumRepository var zypperRepos []*agentendpointpb.ZypperRepository var gooRepos []*agentendpointpb.GooRepository for _, repo := range egp.GetPackageRepositories() { if r := repo.GetPackageRepository().GetGoo(); r != nil { gooRepos = append(gooRepos, r) continue } if r := repo.GetPackageRepository().GetApt(); r != nil { aptRepos = append(aptRepos, r) continue } if r := repo.GetPackageRepository().GetYum(); r != nil { yumRepos = append(yumRepos, r) continue } if r := repo.GetPackageRepository().GetZypper(); r != nil { zypperRepos = append(zypperRepos, r) continue } } var gooInstallPkgs, gooRemovePkgs, gooUpdatePkgs []*agentendpointpb.Package var aptInstallPkgs, aptRemovePkgs, aptUpdatePkgs []*agentendpointpb.Package var yumInstallPkgs, yumRemovePkgs, yumUpdatePkgs []*agentendpointpb.Package var zypperInstallPkgs, zypperRemovePkgs, zypperUpdatePkgs []*agentendpointpb.Package for _, pkg := range egp.GetPackages() { switch pkg.GetPackage().GetManager() { case agentendpointpb.Package_ANY, agentendpointpb.Package_MANAGER_UNSPECIFIED: switch pkg.GetPackage().GetDesiredState() { case agentendpointpb.DesiredState_INSTALLED, agentendpointpb.DesiredState_DESIRED_STATE_UNSPECIFIED: gooInstallPkgs = append(gooInstallPkgs, pkg.GetPackage()) aptInstallPkgs = append(aptInstallPkgs, pkg.GetPackage()) yumInstallPkgs = append(yumInstallPkgs, pkg.GetPackage()) zypperInstallPkgs = append(zypperInstallPkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_REMOVED: gooRemovePkgs = append(gooRemovePkgs, pkg.GetPackage()) aptRemovePkgs = append(aptRemovePkgs, pkg.GetPackage()) yumRemovePkgs = append(yumRemovePkgs, pkg.GetPackage()) zypperRemovePkgs = append(zypperRemovePkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_UPDATED: gooUpdatePkgs = append(gooUpdatePkgs, pkg.GetPackage()) aptUpdatePkgs = append(aptUpdatePkgs, pkg.GetPackage()) yumUpdatePkgs = append(yumUpdatePkgs, pkg.GetPackage()) zypperUpdatePkgs = append(zypperUpdatePkgs, pkg.GetPackage()) } case agentendpointpb.Package_GOO: switch pkg.GetPackage().GetDesiredState() { case agentendpointpb.DesiredState_INSTALLED, agentendpointpb.DesiredState_DESIRED_STATE_UNSPECIFIED: gooInstallPkgs = append(gooInstallPkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_REMOVED: gooRemovePkgs = append(gooRemovePkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_UPDATED: gooUpdatePkgs = append(gooUpdatePkgs, pkg.GetPackage()) } case agentendpointpb.Package_APT: switch pkg.GetPackage().GetDesiredState() { case agentendpointpb.DesiredState_INSTALLED, agentendpointpb.DesiredState_DESIRED_STATE_UNSPECIFIED: aptInstallPkgs = append(aptInstallPkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_REMOVED: aptRemovePkgs = append(aptRemovePkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_UPDATED: aptUpdatePkgs = append(aptUpdatePkgs, pkg.GetPackage()) } case agentendpointpb.Package_YUM: switch pkg.GetPackage().GetDesiredState() { case agentendpointpb.DesiredState_INSTALLED, agentendpointpb.DesiredState_DESIRED_STATE_UNSPECIFIED: yumInstallPkgs = append(yumInstallPkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_REMOVED: yumRemovePkgs = append(yumRemovePkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_UPDATED: yumUpdatePkgs = append(yumUpdatePkgs, pkg.GetPackage()) } case agentendpointpb.Package_ZYPPER: switch pkg.GetPackage().GetDesiredState() { case agentendpointpb.DesiredState_INSTALLED, agentendpointpb.DesiredState_DESIRED_STATE_UNSPECIFIED: zypperInstallPkgs = append(zypperInstallPkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_REMOVED: zypperRemovePkgs = append(zypperRemovePkgs, pkg.GetPackage()) case agentendpointpb.DesiredState_UPDATED: zypperUpdatePkgs = append(zypperUpdatePkgs, pkg.GetPackage()) } } } if packages.GooGetExists { if err := googetRepositories(ctx, gooRepos, agentconfig.GooGetRepoFilePath()); err != nil { clog.Errorf(ctx, "Error writing googet repo file: %v", err) } if err := retryutil.RetryFunc(ctx, 1*time.Minute, "Applying googet changes", func() error { return googetChanges(ctx, gooInstallPkgs, gooRemovePkgs, gooUpdatePkgs) }); err != nil { clog.Errorf(ctx, "Error performing googet changes: %v", err) } } if packages.AptExists { if err := aptRepositories(ctx, aptRepos, agentconfig.AptRepoFilePath()); err != nil { clog.Errorf(ctx, "Error writing apt repo file: %v", err) } if err := retryutil.RetryFunc(ctx, 1*time.Minute, "Applying apt changes", func() error { return aptChanges(ctx, aptInstallPkgs, aptRemovePkgs, aptUpdatePkgs) }); err != nil { clog.Errorf(ctx, "Error performing apt changes: %v", err) } } if packages.YumExists { if err := yumRepositories(ctx, yumRepos, agentconfig.YumRepoFilePath()); err != nil { clog.Errorf(ctx, "Error writing yum repo file: %v", err) } if err := retryutil.RetryFunc(ctx, 1*time.Minute, "Applying yum changes", func() error { return yumChanges(ctx, yumInstallPkgs, yumRemovePkgs, yumUpdatePkgs) }); err != nil { clog.Errorf(ctx, "Error performing yum changes: %v", err) } } if packages.ZypperExists { if err := zypperRepositories(ctx, zypperRepos, agentconfig.ZypperRepoFilePath()); err != nil { clog.Errorf(ctx, "Error writing zypper repo file: %v", err) } if err := retryutil.RetryFunc(ctx, 1*time.Minute, "Applying zypper changes.", func() error { return zypperChanges(ctx, zypperInstallPkgs, zypperRemovePkgs, zypperUpdatePkgs) }); err != nil { clog.Errorf(ctx, "Error performing zypper changes: %v", err) } } } func checksum(r io.Reader) hash.Hash { hash := sha256.New() io.Copy(hash, r) return hash } func writeIfChanged(ctx context.Context, content []byte, path string) error { file, err := os.Open(path) if err != nil && !os.IsNotExist(err) { return err } if !os.IsNotExist(err) { reader := bytes.NewReader(content) h1 := checksum(reader) h2 := checksum(file) file.Close() if bytes.Equal(h1.Sum(nil), h2.Sum(nil)) { return nil } } clog.Infof(ctx, "Writing repo file %s with updated contents", path) return util.AtomicWrite(path, content, 0644) }