From 568d37106ff752cd142a73168853476ccbe06a48 Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Tue, 2 Jan 2024 16:08:50 +0100 Subject: [PATCH 1/4] Move to external go-i18n and gotext We did the work to avoid our internal/ package last cycle. Move now to it. We still need to validate each lines that we log or not. --- internal/ad/ad.go | 48 ++-- internal/ad/admxgen/admxgen.go | 44 +-- internal/ad/admxgen/common/common.go | 5 +- internal/ad/admxgen/dconf/dconf.go | 25 +- internal/ad/admxgen/files.go | 9 +- internal/ad/backends/backend.go | 4 +- internal/ad/backends/sss/sss.go | 12 +- internal/ad/backends/winbind/winbind.go | 16 +- internal/ad/common/common.go | 4 +- internal/ad/definitions.go | 4 +- internal/ad/download.go | 28 +- internal/ad/registry/registry.go | 8 +- internal/adsysservice/adsysservice.go | 9 +- internal/adsysservice/client.go | 4 +- internal/adsysservice/doc.go | 15 +- internal/adsysservice/policy.go | 12 +- internal/adsysservice/service.go | 47 ++-- internal/adsysservice/version.go | 4 +- internal/authorizer/authorizer.go | 40 +-- internal/authorizer/servercreds.go | 13 +- internal/cmdhandler/cmdhandler.go | 10 +- internal/cmdhandler/suggest.go | 11 +- internal/config/config.go | 6 +- internal/config/watchd/watchd.go | 20 +- internal/daemon/daemon.go | 25 +- internal/grpc/grpcerror/grpcerror.go | 11 +- internal/grpc/logstreamer/log.go | 7 +- internal/grpc/logstreamer/server.go | 18 +- internal/i18n/export_test.go | 21 -- internal/i18n/generate-locales.go | 256 ------------------ internal/i18n/i18n.go | 97 ------- internal/i18n/i18n_test.go | 193 ------------- internal/policies/apparmor/apparmor.go | 66 ++--- internal/policies/certificate/certificate.go | 17 +- internal/policies/dconf/dconf.go | 28 +- internal/policies/gdm/gdm.go | 6 +- internal/policies/manager.go | 31 ++- internal/policies/mount/mount.go | 58 ++-- internal/policies/policies.go | 23 +- internal/policies/privilege/privilege.go | 8 +- internal/policies/proxy/proxy.go | 8 +- internal/policies/scripts/scripts.go | 34 +-- internal/stdforward/stdforward.go | 4 +- internal/systemd/systemd.go | 16 +- internal/watchdservice/watchdservice.go | 65 +++-- internal/watchdservice/watchdservice_linux.go | 5 +- internal/watchdtui/watchdtui.go | 33 ++- internal/watcher/watcher.go | 59 ++-- 48 files changed, 464 insertions(+), 1023 deletions(-) delete mode 100644 internal/i18n/export_test.go delete mode 100644 internal/i18n/generate-locales.go delete mode 100644 internal/i18n/i18n.go delete mode 100644 internal/i18n/i18n_test.go diff --git a/internal/ad/ad.go b/internal/ad/ad.go index 3a6c8e300..37f84bfa8 100644 --- a/internal/ad/ad.go +++ b/internal/ad/ad.go @@ -20,12 +20,12 @@ import ( "sync" "time" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/ad/backends" adcommon "github.com/ubuntu/adsys/internal/ad/common" "github.com/ubuntu/adsys/internal/ad/registry" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/adsys/internal/smbsafe" @@ -117,7 +117,7 @@ var AdsysGpoListCode string // New returns an AD object to manage concurrency, with a local kr5 ticket from machine keytab. func New(ctx context.Context, configBackend backends.Backend, hostname string, opts ...Option) (ad *AD, err error) { - defer decorate.OnError(&err, i18n.G("can't create Active Directory object")) + defer decorate.OnError(&err, gotext.Get("can't create Active Directory object")) versionID, err := adcommon.GetVersionID("/") if err != nil { @@ -155,7 +155,7 @@ func New(ctx context.Context, configBackend backends.Backend, hostname string, o domain := configBackend.Domain() serverFQDN, err := configBackend.ServerFQDN(ctx) if err != nil && !errors.Is(err, backends.ErrNoActiveServer) { - return nil, fmt.Errorf(i18n.G("can't get current Server FQDN: %w"), err) + return nil, errors.New(gotext.Get("can't get current Server FQDN: %v", err)) } log.Debugf(ctx, "Backend is SSSD. AD domain: %q, server from configuration: %q", domain, serverFQDN) @@ -180,16 +180,16 @@ func New(ctx context.Context, configBackend backends.Backend, hostname string, o // The GPOs are returned from the highest priority in the hierarchy, with enforcement in reverse order // to the lowest priority. func (ad *AD) GetPolicies(ctx context.Context, objectName string, objectClass ObjectClass, userKrb5CCName string) (pols policies.Policies, err error) { - defer decorate.OnError(&err, i18n.G("can't get policies for %q"), objectName) + defer decorate.OnError(&err, gotext.Get("can't get policies for %q", objectName)) log.Debugf(ctx, "GetPolicies for %q, type %q", objectName, objectClass) if objectClass == UserObject && !strings.Contains(objectName, "@") { - return pols, fmt.Errorf(i18n.G("user name %q should be of the form %s@DOMAIN"), objectName, objectName) + return pols, errors.New(gotext.Get("user name %q should be of the form %s@DOMAIN", objectName, objectName)) } if objectClass == ComputerObject && objectName != ad.hostname { - return pols, fmt.Errorf(i18n.G("requested a type computer of %q which isn't current host %q"), objectName, ad.hostname) + return pols, errors.New(gotext.Get("requested a type computer of %q which isn't current host %q", objectName, ad.hostname)) } krb5CCPath := filepath.Join(ad.krb5CacheDir, objectName) @@ -225,7 +225,7 @@ func (ad *AD) GetPolicies(ctx context.Context, objectName string, objectClass Ob if !online { var cachedPolicies policies.Policies if cachedPolicies, err = policies.NewFromCache(ctx, filepath.Join(ad.policiesCacheDir, objectName)); err != nil { - return cachedPolicies, fmt.Errorf(i18n.G("machine is offline and policies cache is unavailable: %v"), err) + return cachedPolicies, errors.New(gotext.Get("machine is offline and policies cache is unavailable: %v", err)) } log.Infof(ctx, "Can't reach AD: machine is offline and %q policies are applied using previous online update", objectName) @@ -235,7 +235,7 @@ func (ad *AD) GetPolicies(ctx context.Context, objectName string, objectClass Ob // We need an AD DC to connect to adServerFQDN, err := ad.configBackend.ServerFQDN(ctx) if err != nil { - return policies.Policies{}, fmt.Errorf(i18n.G("can't get current Server FQDN: %w"), err) + return policies.Policies{}, errors.New(gotext.Get("can't get current Server FQDN: %v", err)) } // Otherwise, try fetching the GPO list from LDAP @@ -256,7 +256,7 @@ func (ad *AD) GetPolicies(ctx context.Context, objectName string, objectClass Ob err = cmd.Run() smbsafe.DoneExec() if err != nil { - return pols, fmt.Errorf(i18n.G("failed to retrieve the list of GPO (exited with %d): %v\n%s"), cmd.ProcessState.ExitCode(), err, stderr.String()) + return pols, errors.New(gotext.Get("failed to retrieve the list of GPO (exited with %d): %v\n%s", cmd.ProcessState.ExitCode(), err, stderr.String())) } downloadables := make(map[string]string) @@ -347,7 +347,7 @@ func (ad *AD) GetPolicies(ctx context.Context, objectName string, objectClass Ob // ListUsers returns the list of users on the system based on their cached policy information. // If active is true, the list of users is retrieved from the cached Kerberos ticket information. func (ad *AD) ListUsers(ctx context.Context, active bool) (users []string, err error) { - defer decorate.OnError(&err, i18n.G("can't list users from cache")) + defer decorate.OnError(&err, gotext.Get("can't list users from cache")) log.Debug(ctx, "ListUsers") @@ -361,7 +361,7 @@ func (ad *AD) ListUsers(ctx context.Context, active bool) (users []string, err e entries, err := os.ReadDir(cacheDir) if err != nil { - return users, fmt.Errorf(i18n.G("failed to read cache directory: %v"), err) + return users, errors.New(gotext.Get("failed to read cache directory: %v", err)) } for _, entry := range entries { @@ -390,7 +390,7 @@ func (ad *AD) ensureKrb5CCSymlink(srcKrb5CCName, dstKrb5CCName string) error { srcKrb5CCName, err := filepath.Abs(srcKrb5CCName) if err != nil { - return fmt.Errorf(i18n.G("can't get absolute path of ccname to symlink to: %v"), err) + return errors.New(gotext.Get("can't get absolute path of ccname to symlink to: %v", err)) } src, err := os.Readlink(dstKrb5CCName) @@ -401,12 +401,12 @@ func (ad *AD) ensureKrb5CCSymlink(srcKrb5CCName, dstKrb5CCName string) error { } // Delete the symlink to create a new one. if err := os.Remove(dstKrb5CCName); err != nil { - return fmt.Errorf(i18n.G("failed to remove existing symlink: %v"), err) + return errors.New(gotext.Get("failed to remove existing symlink: %v", err)) } } if err := os.MkdirAll(filepath.Dir(dstKrb5CCName), 0700); err != nil { - return fmt.Errorf(i18n.G("failed to create parent directory for symlink: %w"), err) + return errors.New(gotext.Get("failed to create parent directory for symlink: %v", err)) } if err := os.Symlink(srcKrb5CCName, dstKrb5CCName); err != nil { @@ -428,14 +428,14 @@ func (ad *AD) ensureKrb5CCCopy(krb5CCSymlink, krb5CCCopyName string) error { krb5CCSrc, err := os.Readlink(krb5CCSymlink) if err != nil { - return fmt.Errorf(i18n.G("failed to read krb5cc symlink: %w"), err) + return errors.New(gotext.Get("failed to read krb5cc symlink: %v", err)) } if copyStat, err := os.Lstat(krb5CCCopyName); err == nil && copyStat.Mode()&os.ModeSymlink == 0 { // We already have a copy of the ticket, let's check if we need to update it srcStat, err := os.Stat(krb5CCSrc) if err != nil { - return fmt.Errorf(i18n.G("failed to stat source ticket: %w"), err) + return errors.New(gotext.Get("failed to stat source ticket: %v", err)) } // The source ticket is not newer than the destination one, no need to update @@ -459,7 +459,7 @@ func (ad *AD) ensureKrb5CCCopy(krb5CCSymlink, krb5CCCopyName string) error { func safeCopyFile(src, dst string, mode os.FileMode) error { content, err := os.ReadFile(src) if err != nil { - return fmt.Errorf(i18n.G("failed to read source file: %w"), err) + return errors.New(gotext.Get("failed to read source file: %v", err)) } if err := os.WriteFile(dst+".new", content, mode); err != nil { return err @@ -522,7 +522,7 @@ func (ad *AD) parseGPOs(ctx context.Context, gpos []gpo, objectClass ObjectClass // Decode and apply policies in gpo order. First win pols, err := registry.DecodePolicy(f) if err != nil { - return fmt.Errorf(i18n.G("%s: %v"), f.Name(), err) + return errors.New(gotext.Get("%s: %v", f.Name(), err)) } // filter keys to be overridden @@ -544,7 +544,7 @@ func (ad *AD) parseGPOs(ctx context.Context, gpos []gpo, objectClass ObjectClass continue } if pol.Err != nil { - return fmt.Errorf(i18n.G("%s: %v"), f.Name(), pol.Err) + return errors.New(gotext.Get("%s: %v", f.Name(), pol.Err)) } pol.Key = strings.TrimPrefix(pol.Key, keyFilterPrefix) @@ -600,9 +600,9 @@ func (ad *AD) GetInfo(ctx context.Context) (msg string) { var online string if isOnline, err := ad.configBackend.IsOnline(); err != nil { log.Warning(ctx, err) - online = fmt.Sprint(i18n.G("**Can't check if we have an active connection**\n")) + online = fmt.Sprint(gotext.Get("**Can't check if we have an active connection**\n")) } else if !isOnline { - online = fmt.Sprint(i18n.G("**Offline mode** using cached policies\n")) + online = fmt.Sprint(gotext.Get("**Offline mode** using cached policies\n")) } domain := ad.configBackend.Domain() server, err := ad.configBackend.ServerFQDN(ctx) @@ -610,7 +610,7 @@ func (ad *AD) GetInfo(ctx context.Context) (msg string) { server = "Unknown" } - return fmt.Sprintf(i18n.G("%s\n%sDomain: %s\nServer FQDN: %s"), config, online, domain, server) + return gotext.Get("%s\n%sDomain: %s\nServer FQDN: %s", config, online, domain, server) } // NormalizeTargetName transforms the specified target to values adsys knows. @@ -648,10 +648,10 @@ func (ad *AD) NormalizeTargetName(ctx context.Context, target string, objectClas case 1: baseUser = c[0] default: - return "", fmt.Errorf(i18n.G(`only one \ is permitted in domain\username. Got: %s`), target) + return "", errors.New(gotext.Get(`only one \ is permitted in domain\username. Got: %s`, target)) } if domainSuffix == "" && ad.configBackend.DefaultDomainSuffix() == "" { - return "", fmt.Errorf(i18n.G(`no domain provided for user %q and no default domain in sssd.conf`), target) + return "", errors.New(gotext.Get(`no domain provided for user %q and no default domain in sssd.conf`, target)) } if domainSuffix == "" { domainSuffix = ad.configBackend.DefaultDomainSuffix() diff --git a/internal/ad/admxgen/admxgen.go b/internal/ad/admxgen/admxgen.go index 0ec53159a..e92ab527f 100644 --- a/internal/ad/admxgen/admxgen.go +++ b/internal/ad/admxgen/admxgen.go @@ -48,10 +48,10 @@ import ( "strings" "text/template" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/ad/admxgen/common" adcommon "github.com/ubuntu/adsys/internal/ad/common" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" "golang.org/x/text/cases" @@ -99,19 +99,19 @@ type generator struct { var ( // defaultAppendNote is the default note for append-type policies. It will be used unless a specific note is provided. - defaultAppendNote = i18n.G(` + defaultAppendNote = gotext.Get(` * Enabled: The value(s) referenced in the entry are applied on the client machine. * Disabled: The value(s) are removed from the target machine. * Not configured: Value(s) declared higher in the GPO hierarchy will be used if available.`) // defaultOverrideNote is the default note for override-type policies. It will be used unless a specific note is provided. - defaultOverrideNote = i18n.G(` + defaultOverrideNote = gotext.Get(` * Enabled: The value(s) referenced in the entry are applied on the client machine. * Disabled: The value(s) are removed from the target machine.`) ) func (g generator) generateExpandedCategories(categories []category, policies []common.ExpandedPolicy, allowMissingKeys bool) (ep []expandedCategory, err error) { - defer decorate.OnError(&err, i18n.G("can't generate expanded categories")) + defer decorate.OnError(&err, gotext.Get("can't generate expanded categories")) // noPoliciesOn is a map to attest that each release was assigned at least one property noPoliciesOn := make(map[string]struct{}) @@ -203,7 +203,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] if supportedOn == "" { if release != "all" { - supportedOn = fmt.Sprintf(i18n.G("Supported on %s %s"), g.distroID, release) + supportedOn = gotext.Get("Supported on %s %s", g.distroID, release) } } else { supportedOn = fmt.Sprintf("%s, %s", supportedOn, release) @@ -216,7 +216,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] } defaultString = p.Default - defaults = append(defaults, fmt.Sprintf(i18n.G("- Default for %s: %s"), release, p.Default)) + defaults = append(defaults, gotext.Get("- Default for %s: %s", release, p.Default)) if release > highestRelease { highestRelease = release @@ -254,10 +254,10 @@ func (g generator) generateExpandedCategories(categories []category, policies [] explainText = fmt.Sprintf("%s\n%s", explainText, strings.Join(defaults, "\n")) } else if defaultString != "" { // All defaults are the same and not empty - explainText = fmt.Sprintf("%s\n%s", explainText, fmt.Sprintf(i18n.G("- Default: %s"), defaultString)) + explainText = fmt.Sprintf("%s\n%s", explainText, gotext.Get("- Default: %s", defaultString)) } - explainText = fmt.Sprintf(i18n.G("%s\n\nNote:"), explainText) + explainText = gotext.Get("%s\n\nNote:", explainText) var note string if releasesElements["all"].Note != "" { note = releasesElements["all"].Note @@ -280,13 +280,13 @@ func (g generator) generateExpandedCategories(categories []category, policies [] // Mention if any of the policies require Ubuntu Pro // Currently this only applies to non-dconf policies if typePol != dconfPolicyType { - explainText = fmt.Sprintf("%s\n\n%s", explainText, i18n.G("An Ubuntu Pro subscription on the client is required to apply this policy.")) + explainText = fmt.Sprintf("%s\n\n%s", explainText, gotext.Get("An Ubuntu Pro subscription on the client is required to apply this policy.")) } // prepare meta for the whole policy metaEnabled, err := json.Marshal(metasEnabled) if err != nil { - return nil, errors.New(i18n.G("failed to marshal enabled meta data")) + return nil, errors.New(gotext.Get("failed to marshal enabled meta data")) } // We can’t have metaEnabled or metaDisabled being strictly equals: // some AD servers thinks they that disabled means @@ -296,7 +296,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] } metaDisabled, err := json.Marshal(metasDisabled) if err != nil { - return nil, errors.New(i18n.G("failed to marshal disabled meta data")) + return nil, errors.New(gotext.Get("failed to marshal disabled meta data")) } mergedPolicies[key] = mergedPolicy{ @@ -315,7 +315,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] for r := range noPoliciesOn { releases = append(releases, r) } - return nil, fmt.Errorf(i18n.G("some releases have no policies attached to them while being listed in categories: %v"), releases) + return nil, errors.New(gotext.Get("some releases have no policies attached to them while being listed in categories: %v", releases)) } // 2. Inflate policies in categories, keep policy order from category list @@ -325,7 +325,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] var policies []mergedPolicy if cat.DefaultPolicyClass == "" { - return expandedCategory{}, fmt.Errorf(i18n.G("%s needs a default policy class"), cat.DisplayName) + return expandedCategory{}, errors.New(gotext.Get("%s needs a default policy class", cat.DisplayName)) } defaultPolicyClass, err := common.ValidClass(cat.DefaultPolicyClass) if err != nil { @@ -340,12 +340,12 @@ func (g generator) generateExpandedCategories(categories []category, policies [] for _, p := range cat.Policies { pol, ok := mergedPolicies[p] if !ok { - msg := fmt.Sprintf(i18n.G("policy %s referenced in %q does not exist in any supported releases"), p, cat.DisplayName) + err := errors.New(gotext.Get("policy %s referenced in %q does not exist in any supported releases", p, cat.DisplayName)) if allowMissingKeys { - log.Warningf(context.Background(), msg) + log.Warning(context.Background(), err) continue } - return expandedCategory{}, errors.New(msg) + return expandedCategory{}, err } if pol.Class == "" { pol.Class = defaultPolicyClass @@ -386,7 +386,7 @@ func (g generator) generateExpandedCategories(categories []category, policies [] // Check that all policies are at least attached once if len(unattachedPolicies) > 0 { - return nil, fmt.Errorf(i18n.G("the following policies have not been assigned to a category: %v"), unattachedPolicies) + return nil, errors.New(gotext.Get("the following policies have not been assigned to a category: %v", unattachedPolicies)) } return expandedCategories, nil @@ -459,7 +459,7 @@ func (g generator) toID(key string, s ...string) string { } func (g generator) expandedCategoriesToADMX(expandedCategories []expandedCategory, dest string) (err error) { - defer decorate.OnError(&err, i18n.G("can't generate ADMX files")) + defer decorate.OnError(&err, gotext.Get("can't generate ADMX files")) var inputCategories []categoryForADMX var inputPolicies []policyForADMX @@ -476,7 +476,7 @@ func (g generator) expandedCategoriesToADMX(expandedCategories []expandedCategor }{g.distroID, inputCategories, inputPolicies} if err := os.MkdirAll(dest, 0750); err != nil { - return fmt.Errorf(i18n.G("can't create destination directory for AD policies: %v"), err) + return errors.New(gotext.Get("can't create destination directory for AD policies: %v", err)) } funcMap := template.FuncMap{ @@ -487,7 +487,7 @@ func (g generator) expandedCategoriesToADMX(expandedCategories []expandedCategor f, err := os.Create(filepath.Join(dest, g.distroID+".admx")) if err != nil { - return fmt.Errorf(i18n.G("can't create admx file: %v"), err) + return errors.New(gotext.Get("can't create admx file: %v", err)) } defer decorate.LogFuncOnError(f.Close) t := template.Must(template.New("admx.template").Funcs(funcMap).Parse(admxTemplate)) @@ -500,7 +500,7 @@ func (g generator) expandedCategoriesToADMX(expandedCategories []expandedCategor f, err = os.Create(filepath.Join(dest, g.distroID+".adml")) if err != nil { - return fmt.Errorf(i18n.G("can't create admx file: %v"), err) + return errors.New(gotext.Get("can't create admx file: %v", err)) } defer decorate.LogFuncOnError(f.Close) t = template.Must(template.New("adml.template").Funcs(funcMap).Parse(admlTemplate)) @@ -578,7 +578,7 @@ func expandedCategoriesToMD(expandedCategories []expandedCategory, rootDest stri f, err := os.Create(filepath.Join(dest, filepath.Base(polDetails.Key)) + ".md") if err != nil { - return fmt.Errorf(i18n.G("can't create md file: %v"), err) + return errors.New(gotext.Get("can't create md file: %v", err)) } defer decorate.LogFuncOnError(f.Close) t := template.Must(template.New("doc policy").Parse(docPolicyTemplate)) diff --git a/internal/ad/admxgen/common/common.go b/internal/ad/admxgen/common/common.go index aee4822a9..28d662ff7 100644 --- a/internal/ad/admxgen/common/common.go +++ b/internal/ad/admxgen/common/common.go @@ -2,9 +2,10 @@ package common import ( + "errors" "fmt" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -77,7 +78,7 @@ func ValidClass(class string) (string, error) { c := cases.Title(language.Und, cases.NoLower).String(class) if c != "" && c != "User" && c != "Machine" { - return "", fmt.Errorf(i18n.G("invalid class %q"), class) + return "", errors.New(gotext.Get("invalid class %q", class)) } return c, nil diff --git a/internal/ad/admxgen/dconf/dconf.go b/internal/ad/admxgen/dconf/dconf.go index a80fb7c7a..b9bddfea5 100644 --- a/internal/ad/admxgen/dconf/dconf.go +++ b/internal/ad/admxgen/dconf/dconf.go @@ -3,6 +3,7 @@ package dconf import ( "encoding/xml" + "errors" "fmt" "io" "math" @@ -12,9 +13,9 @@ import ( "strconv" "strings" + "github.com/leonelquinteros/gotext" log "github.com/sirupsen/logrus" "github.com/ubuntu/adsys/internal/ad/admxgen/common" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "gopkg.in/ini.v1" ) @@ -53,7 +54,7 @@ var ( // Generate creates a set of expanded policies from a list of policies and // dconf schemas available on the machine. func Generate(policies []Policy, release string, root, currentSessions string) (ep []common.ExpandedPolicy, err error) { - defer decorate.OnError(&err, i18n.G("can't generate dconf expanded policies")) + defer decorate.OnError(&err, gotext.Get("can't generate dconf expanded policies")) s, d, err := loadSchemasFromDisk(filepath.Join(root, schemasPath)) if err != nil { @@ -127,7 +128,7 @@ func inflateToExpandedPolicies(policies []Policy, release, currentSessions strin Class: class, Release: release, Default: defaultVal, - Note: i18n.G(`default system value is used for "Not Configured" and enforced if "Disabled".`), + Note: gotext.Get(`default system value is used for "Not Configured" and enforced if "Disabled".`), Type: "dconf", RangeValues: s.RangeValues, Choices: s.Choices, @@ -159,11 +160,11 @@ func inflateToExpandedPolicies(policies []Policy, release, currentSessions strin min = "0" } if min == "NaN" || min == "Inf" { - return nil, fmt.Errorf(i18n.G("min value for long decimal is not a valid float: %s"), min) + return nil, errors.New(gotext.Get("min value for long decimal is not a valid float: %s", min)) } s, err := strconv.ParseFloat(min, 64) if err != nil { - return nil, fmt.Errorf(i18n.G("min value for long decimal is not a valid float: %v"), err) + return nil, errors.New(gotext.Get("min value for long decimal is not a valid float: %v", err)) } min = fmt.Sprintf("%f", math.Max(0, s)) ep.RangeValues.Min = min @@ -227,7 +228,7 @@ type schemaList struct { } func loadSchemasFromDisk(path string) (entries map[string]schemaEntry, defaultsForPath map[string]string, err error) { - defer decorate.OnError(&err, i18n.G("error while loading schemas")) + defer decorate.OnError(&err, gotext.Get("error while loading schemas")) entries = make(map[string]schemaEntry) enums := make(map[string][]string) @@ -236,24 +237,24 @@ func loadSchemasFromDisk(path string) (entries map[string]schemaEntry, defaultsF // load schemas schemas, err := filepath.Glob(filepath.Join(path, "*.xml")) if err != nil { - return nil, nil, fmt.Errorf(i18n.G("failed to read list of schemas: %w"), err) + return nil, nil, errors.New(gotext.Get("failed to read list of schemas: %v", err)) } for _, p := range schemas { f, err := os.Open(filepath.Clean(p)) if err != nil { - return nil, nil, fmt.Errorf(i18n.G("cannot open file: %w"), err) + return nil, nil, errors.New(gotext.Get("cannot open file: %v", err)) } defer decorate.LogFuncOnError(f.Close) d, err := io.ReadAll(f) if err != nil { - return nil, nil, fmt.Errorf(i18n.G("cannot read schema data: %w"), err) + return nil, nil, errors.New(gotext.Get("cannot read schema data: %v", err)) } var sl schemaList if err := xml.Unmarshal(d, &sl); err != nil { - return nil, nil, fmt.Errorf(i18n.G("%s is an invalid schema: %v"), p, err) + return nil, nil, errors.New(gotext.Get("%s is an invalid schema: %v", p, err)) } for _, s := range sl.Schema { @@ -323,7 +324,7 @@ func loadSchemasFromDisk(path string) (entries map[string]schemaEntry, defaultsF if e.enumID != "" { var ok bool if e.Choices, ok = enums[e.enumID]; !ok { - return nil, nil, fmt.Errorf(i18n.G("enum id %s referenced by %s doesn't exist in list of enums"), e.enumID, e.Schema) + return nil, nil, errors.New(gotext.Get("enum id %s referenced by %s doesn't exist in list of enums", e.enumID, e.Schema)) } e.enumID = "" entries[k] = e @@ -333,7 +334,7 @@ func loadSchemasFromDisk(path string) (entries map[string]schemaEntry, defaultsF // Load override files to override defaults overrides, err := filepath.Glob(filepath.Join(path, "*.gschema.override")) if err != nil { - return nil, nil, fmt.Errorf(i18n.G("failed to read overrides files: %w"), err) + return nil, nil, errors.New(gotext.Get("failed to read overrides files: %v", err)) } sort.Strings(overrides) diff --git a/internal/ad/admxgen/files.go b/internal/ad/admxgen/files.go index 58f7efd23..ac10f0ca9 100644 --- a/internal/ad/admxgen/files.go +++ b/internal/ad/admxgen/files.go @@ -2,16 +2,17 @@ package admxgen import ( _ "embed" + "errors" "fmt" "os" "path/filepath" "sort" "strings" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/ad/admxgen/common" "github.com/ubuntu/adsys/internal/ad/admxgen/dconf" adcommon "github.com/ubuntu/adsys/internal/ad/common" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "golang.org/x/sync/errgroup" "gopkg.in/yaml.v3" @@ -34,12 +35,12 @@ func Expand(src, dst, root, currentSession string) error { } if _, err = os.Stat(src); err != nil { - return fmt.Errorf(i18n.G("failed to access definition files: %w"), err) + return errors.New(gotext.Get("failed to access definition files: %v", err)) } // Expand policies for all supported yaml files files, err := filepath.Glob(filepath.Join(src, "*.yaml")) if err != nil { - return fmt.Errorf(i18n.G("failed to read list of definition files: %w"), err) + return errors.New(gotext.Get("failed to read list of definition files: %v", err)) } expandedPoliciesStream := make(chan []common.ExpandedPolicy, len(files)) @@ -198,7 +199,7 @@ func GenerateDoc(categoryDefinition, src, dst string) error { } func loadDefinitions(categoryDefinition, src string) (ep []common.ExpandedPolicy, cfs categoryFileStruct, err error) { - defer decorate.OnError(&err, i18n.G("can't load category definition")) + defer decorate.OnError(&err, gotext.Get("can't load category definition")) var nilCategoryFileStruct categoryFileStruct diff --git a/internal/ad/backends/backend.go b/internal/ad/backends/backend.go index 8db0bce5e..f7c8d4282 100644 --- a/internal/ad/backends/backend.go +++ b/internal/ad/backends/backend.go @@ -5,7 +5,7 @@ import ( "context" "errors" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" ) // Backend is the common interface for all backends. @@ -30,5 +30,5 @@ type Backend interface { var ( // ErrNoActiveServer is an error receive when there is no active server and no static configuration // This is received in ServerFQDN. - ErrNoActiveServer = errors.New(i18n.G("no active server found")) + ErrNoActiveServer = errors.New(gotext.Get("no active server found")) ) diff --git a/internal/ad/backends/sss/sss.go b/internal/ad/backends/sss/sss.go index 200f30045..f05088fe4 100644 --- a/internal/ad/backends/sss/sss.go +++ b/internal/ad/backends/sss/sss.go @@ -9,10 +9,10 @@ import ( "strings" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/ad/backends" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "gopkg.in/ini.v1" ) @@ -37,7 +37,7 @@ type Config struct { // New returns a sss backend loaded from Config. func New(ctx context.Context, c Config, bus *dbus.Conn) (s SSS, err error) { - defer decorate.OnError(&err, i18n.G("can't get domain configuration from %+v"), c) + defer decorate.OnError(&err, gotext.Get("can't get domain configuration from %+v", c)) log.Debug(ctx, "Loading SSS configuration for AD backend") @@ -57,11 +57,11 @@ func New(ctx context.Context, c Config, bus *dbus.Conn) (s SSS, err error) { // Take first domain as domain for machine and all users sssdDomain := strings.Split(cfg.Section("sssd").Key("domains").String(), ",")[0] if sssdDomain == "" { - return SSS{}, errors.New(i18n.G("failed to find default sssd domain in sssd.conf")) + return SSS{}, errors.New(gotext.Get("failed to find default sssd domain in sssd.conf")) } domain := cfg.Section(fmt.Sprintf("domain/%s", sssdDomain)).Key("ad_domain").String() if domain == "" { - return SSS{}, fmt.Errorf(i18n.G("could not find AD domain name corresponding to %q"), sssdDomain) + return SSS{}, errors.New(gotext.Get("could not find AD domain name corresponding to %q", sssdDomain)) } if defaultDomainSuffix == "" { @@ -102,7 +102,7 @@ func (sss SSS) Domain() string { // If the dynamic lookup worked, but there is still no server FQDN found (for instance, backend // if offline), the error raised is of type ErrorNoActiveServer. func (sss SSS) ServerFQDN(ctx context.Context) (serverFQDN string, err error) { - defer decorate.OnError(&err, i18n.G("error while trying to look up AD server address on SSSD for %q"), sss.domain) + defer decorate.OnError(&err, gotext.Get("error while trying to look up AD server address on SSSD for %q", sss.domain)) if sss.staticServerFQDN != "" { return sss.staticServerFQDN, nil @@ -134,7 +134,7 @@ func (sss SSS) DefaultDomainSuffix() string { func (sss SSS) IsOnline() (bool, error) { var online bool if err := sss.domainDbus.Call(consts.SSSDDbusInterface+".IsOnline", 0).Store(&online); err != nil { - return false, fmt.Errorf(i18n.G("failed to retrieve offline state from SSSD: %v"), err) + return false, errors.New(gotext.Get("failed to retrieve offline state from SSSD: %v", err)) } return online, nil } diff --git a/internal/ad/backends/winbind/winbind.go b/internal/ad/backends/winbind/winbind.go index db968ed14..b9fd371d4 100644 --- a/internal/ad/backends/winbind/winbind.go +++ b/internal/ad/backends/winbind/winbind.go @@ -61,8 +61,8 @@ import ( "strings" "unsafe" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/smbsafe" "github.com/ubuntu/decorate" ) @@ -93,7 +93,7 @@ type options struct { // New returns a winbind backend loaded from Config. func New(ctx context.Context, c Config, hostname string, opts ...Option) (w Winbind, err error) { - defer decorate.OnError(&err, i18n.G("can't get domain configuration from %+v"), c) + defer decorate.OnError(&err, gotext.Get("can't get domain configuration from %+v", c)) // defaults args := options{ @@ -144,8 +144,8 @@ func (w Winbind) HostKrb5CCName() (string, error) { smbsafe.WaitExec() defer smbsafe.DoneExec() if cmd, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).CombinedOutput(); err != nil { - return "", fmt.Errorf(i18n.G(`could not get krb5 cached ticket for %q: %w: -%s`), principal, err, string(cmd)) + return "", errors.New(gotext.Get(`could not get krb5 cached ticket for %q: %v: +%s`, principal, err, string(cmd))) } return target, nil @@ -160,7 +160,7 @@ func (w Winbind) DefaultDomainSuffix() string { // It returns first any static configuration. If nothing is found, it will fetch // the active server from winbind. func (w Winbind) ServerFQDN(ctx context.Context) (serverFQDN string, err error) { - defer decorate.OnError(&err, i18n.G("error while trying to look up AD server address on winbind")) + defer decorate.OnError(&err, gotext.Get("error while trying to look up AD server address on winbind")) if w.staticServerFQDN != "" { return strings.TrimPrefix(w.staticServerFQDN, "ldap://"), nil @@ -187,7 +187,7 @@ func (w Winbind) IsOnline() (bool, error) { defer C.free(unsafe.Pointer(cDomain)) online, err := C.is_online(cDomain) if err != nil { - err = fmt.Errorf(i18n.G("could not get online status for domain %q: status code %d"), w.domain, err) + err = errors.New(gotext.Get("could not get online status for domain %q: status code %d", w.domain, err)) } return bool(online), err } @@ -195,7 +195,7 @@ func (w Winbind) IsOnline() (bool, error) { func domainName() (string, error) { dc := C.get_domain_name() if dc == nil { - return "", errors.New(i18n.G("could not get domain name")) + return "", errors.New(gotext.Get("could not get domain name")) } defer C.free(unsafe.Pointer(dc)) return C.GoString(dc), nil @@ -206,7 +206,7 @@ func dcName(domain string) (string, error) { defer C.free(unsafe.Pointer(cDomain)) dc := C.get_dc_name(cDomain) if dc == nil { - return "", fmt.Errorf(i18n.G("could not get domain controller name for domain %q"), domain) + return "", errors.New(gotext.Get("could not get domain controller name for domain %q", domain)) } defer C.free(unsafe.Pointer(dc)) return C.GoString(dc), nil diff --git a/internal/ad/common/common.go b/internal/ad/common/common.go index 856268346..c45cae505 100644 --- a/internal/ad/common/common.go +++ b/internal/ad/common/common.go @@ -8,7 +8,7 @@ import ( "path/filepath" "strings" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/decorate" ) @@ -17,7 +17,7 @@ const KeyPrefix = "Software/Policies" // GetVersionID returns from root a the VERSION_ID field of os-release. func GetVersionID(root string) (versionID string, err error) { - defer decorate.OnError(&err, i18n.G("cannot get versionID")) + defer decorate.OnError(&err, gotext.Get("cannot get versionID")) releaseFile := filepath.Join(root, "etc/os-release") diff --git a/internal/ad/definitions.go b/internal/ad/definitions.go index 9ff53955f..e5d82063a 100644 --- a/internal/ad/definitions.go +++ b/internal/ad/definitions.go @@ -4,15 +4,15 @@ import ( "context" "fmt" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" policydefinitions "github.com/ubuntu/adsys/policies" "github.com/ubuntu/decorate" ) // GetPolicyDefinitions returns admx and adml content for the given type t of policies. func GetPolicyDefinitions(ctx context.Context, format, distroID string) (admx string, adml string, err error) { - decorate.OnError(&err, i18n.G("can't get policy definition file")) + decorate.OnError(&err, gotext.Get("can't get policy definition file")) log.Debugf(ctx, "GetPolicyDefinitions for %q (%q)", distroID, format) diff --git a/internal/ad/download.go b/internal/ad/download.go index e11856db4..c31d40d35 100644 --- a/internal/ad/download.go +++ b/internal/ad/download.go @@ -43,9 +43,9 @@ import ( "strings" "sync" + "github.com/leonelquinteros/gotext" "github.com/mvo5/libsmbclient-go" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/smbsafe" "github.com/ubuntu/decorate" "golang.org/x/sync/errgroup" @@ -62,7 +62,7 @@ This should not be called concurrently. It returns if the assets were refreshed or not. */ func (ad *AD) fetch(ctx context.Context, krb5Ticket string, downloadables map[string]string) (assetsWereRefreshed bool, err error) { - defer decorate.OnError(&err, i18n.G("can't download all gpos and assets")) + defer decorate.OnError(&err, gotext.Get("can't download all gpos and assets")) // protect env variable and map creation ad.fetchMu.Lock() @@ -104,7 +104,7 @@ func (ad *AD) fetch(ctx context.Context, krb5Ticket string, downloadables map[st g = ad.downloadables[name] } errg.Go(func() (err error) { - defer decorate.OnError(&err, i18n.G("can't download %q"), g.name) + defer decorate.OnError(&err, gotext.Get("can't download %q", g.name)) smbsafe.WaitSmb() defer smbsafe.DoneSmb() @@ -135,9 +135,9 @@ func (ad *AD) fetch(ctx context.Context, krb5Ticket string, downloadables map[st if !shouldDownload { if g.isAssets { - log.Infof(ctx, i18n.G("Assets directory is already up to date")) + log.Info(ctx, gotext.Get("Assets directory is already up to date")) } else { - log.Infof(ctx, i18n.G("GPO %q is already up to date"), g.name) + log.Info(ctx, gotext.Get("GPO %q is already up to date", g.name)) } return nil @@ -167,7 +167,7 @@ var errNoGPTINI = errors.New("no GPT.INI file") // needsDownload returns if the downloadable should be refreshed. // This is done by comparing GPT.INI Version= content. func needsDownload(ctx context.Context, client *libsmbclient.Client, g *downloadable, localPath string) (updateNeeded bool, err error) { - defer decorate.OnError(&err, i18n.G("can't check if %s needs refreshing"), g.name) + defer decorate.OnError(&err, gotext.Get("can't check if %s needs refreshing", g.name)) g.mu.RLock() defer g.mu.RUnlock() @@ -204,7 +204,7 @@ func needsDownload(ctx context.Context, client *libsmbclient.Client, g *download } func getGPOVersion(ctx context.Context, r io.Reader, downloadableName string) (version int, err error) { - defer decorate.OnError(&err, i18n.G("invalid remote GPT.INI")) + defer decorate.OnError(&err, gotext.Get("invalid remote GPT.INI")) buf, err := io.ReadAll(r) if err != nil { @@ -219,7 +219,7 @@ func getGPOVersion(ctx context.Context, r io.Reader, downloadableName string) (v // If the file exists but doesn't contain a Version key, we log a message and return 0 // This is the case for some Default Domain Policy GPOs if !cfg.Section("General").HasKey("Version") { - log.Infof(ctx, i18n.G("No version key found in GPT.INI for %s, assuming 0"), downloadableName) + log.Info(ctx, gotext.Get("No version key found in GPT.INI for %s, assuming 0", downloadableName)) return 0, nil } @@ -232,7 +232,7 @@ func getGPOVersion(ctx context.Context, r io.Reader, downloadableName string) (v // downloadDir will dl in a temporary directory and only commit it if fully downloaded without any errors. func downloadDir(ctx context.Context, client *libsmbclient.Client, url, dest string) (err error) { - defer decorate.OnError(&err, i18n.G("download %q failed"), url) + defer decorate.OnError(&err, gotext.Get("download %q failed", url)) smbsafe.WaitSmb() defer smbsafe.DoneSmb() @@ -245,7 +245,7 @@ func downloadDir(ctx context.Context, client *libsmbclient.Client, url, dest str // It is a directory: recursive download if err := d.Closedir(); err != nil { - return fmt.Errorf(i18n.G("could not close directory: %v"), err) + return errors.New(gotext.Get("could not close directory: %v", err)) } tmpdest, err := os.MkdirTemp(filepath.Dir(dest), fmt.Sprintf("%s.*", filepath.Base(dest))) @@ -255,7 +255,7 @@ func downloadDir(ctx context.Context, client *libsmbclient.Client, url, dest str // Always to try remove temporary directory, so that in case of any failures, it’s not left behind defer func() { if err := os.RemoveAll(tmpdest); err != nil { - log.Info(ctx, i18n.G("Could not clean up temporary directory:"), err) + log.Info(ctx, gotext.Get("Could not clean up temporary directory:"), err) } }() if err := downloadRecursive(ctx, client, url, tmpdest); err != nil { @@ -305,7 +305,7 @@ func downloadRecursive(ctx context.Context, client *libsmbclient.Client, url, de switch dirent.Type { case libsmbclient.SmbcFile: - log.Debugf(ctx, i18n.G("Downloading %s"), entityURL) + log.Debug(ctx, gotext.Get("Downloading %s", entityURL)) f, err := client.Open(entityURL, 0, 0) if err != nil { return err @@ -343,7 +343,7 @@ func findLocalGPTIni(path string) (string, error) { entries, err := os.ReadDir(path) if err != nil { - return "", fmt.Errorf(i18n.G("could not read directory %q: %w"), path, err) + return "", errors.New(gotext.Get("could not read directory %q: %v", path, err)) } for _, entry := range entries { @@ -352,5 +352,5 @@ func findLocalGPTIni(path string) (string, error) { } } - return "", fmt.Errorf(i18n.G("could not find GPT.INI in %q"), path) + return "", errors.New(gotext.Get("could not find GPT.INI in %q", path)) } diff --git a/internal/ad/registry/registry.go b/internal/ad/registry/registry.go index 8a957895a..89993a710 100644 --- a/internal/ad/registry/registry.go +++ b/internal/ad/registry/registry.go @@ -15,7 +15,7 @@ import ( "strings" "unicode/utf16" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" ) @@ -52,7 +52,7 @@ type meta struct { // DecodePolicy parses a policy stream in registry file format and returns a slice of entries. func DecodePolicy(r io.Reader) (entries []entry.Entry, err error) { - defer decorate.OnError(&err, i18n.G("can't parse policy")) + defer decorate.OnError(&err, gotext.Get("can't parse policy")) ent, err := readPolicy(r) if err != nil { @@ -180,7 +180,7 @@ type policyFileHeader struct { } func readPolicy(r io.Reader) (entries []policyRawEntry, err error) { - defer decorate.OnError(&err, i18n.G("invalid policy")) + defer decorate.OnError(&err, gotext.Get("invalid policy")) validPolicyFileHeader := policyFileHeader{ Signature: 0x67655250, @@ -303,7 +303,7 @@ func decodeUtf16(b []byte) (string, error) { // getMetaValues returns meta values (including empty value) for options. func getMetaValues(data []byte, keypath string) (metaValues map[string]meta, err error) { - defer decorate.OnError(&err, i18n.G("can't decode meta value for %s: %v"), keypath, err) + defer decorate.OnError(&err, gotext.Get("can't decode meta value for %s: %v", keypath, err)) metaValues = make(map[string]meta) v, err := decodeUtf16(data) diff --git a/internal/adsysservice/adsysservice.go b/internal/adsysservice/adsysservice.go index 8c37725e4..557b9fa57 100644 --- a/internal/adsysservice/adsysservice.go +++ b/internal/adsysservice/adsysservice.go @@ -3,12 +3,14 @@ package adsysservice import ( "context" + "errors" "fmt" "os" "strings" "time" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/sirupsen/logrus" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/internal/ad" @@ -22,7 +24,6 @@ import ( "github.com/ubuntu/adsys/internal/grpc/interceptorschain" "github.com/ubuntu/adsys/internal/grpc/logconnections" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies" "github.com/ubuntu/decorate" "google.golang.org/grpc" @@ -189,7 +190,7 @@ func WithWinbindConfig(c winbind.Config) func(o *options) error { // If url or domain is empty, we load the missing parameters from sssd.conf, taking first // domain in the list if not provided. func New(ctx context.Context, opts ...option) (s *Service, err error) { - defer decorate.OnError(&err, i18n.G("couldn't create adsys service")) + defer decorate.OnError(&err, gotext.Get("couldn't create adsys service")) // defaults args := options{} @@ -261,7 +262,7 @@ func New(ctx context.Context, opts ...option) (s *Service, err error) { adBackend, err = winbind.New(ctx, args.winbindConfig, hostname) } if err != nil { - return nil, fmt.Errorf(i18n.G("could not initialize AD backend: %v"), err) + return nil, errors.New(gotext.Get("could not initialize AD backend: %v", err)) } adc, err := ad.New(ctx, adBackend, hostname, adOptions...) @@ -354,7 +355,7 @@ func (s *Service) RegisterGRPCServer(d *daemon.Daemon) *grpc.Server { // Quit cleans every ressources than the service was using. func (s *Service) Quit(ctx context.Context) { if err := s.bus.Close(); err != nil { - log.Warningf(ctx, i18n.G("Can't disconnect system dbus: %v"), err) + log.Warning(ctx, gotext.Get("Can't disconnect system dbus: %v", err)) } } diff --git a/internal/adsysservice/client.go b/internal/adsysservice/client.go index 4200484d5..edce4db44 100644 --- a/internal/adsysservice/client.go +++ b/internal/adsysservice/client.go @@ -4,12 +4,12 @@ import ( "fmt" "time" + "github.com/leonelquinteros/gotext" "github.com/sirupsen/logrus" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/internal/grpc/contextidler" "github.com/ubuntu/adsys/internal/grpc/interceptorschain" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -23,7 +23,7 @@ type AdSysClient struct { // NewClient connect to the socket and returns a new AdSysClient. func NewClient(socket string, timeout time.Duration) (c *AdSysClient, err error) { - defer decorate.OnError(&err, i18n.G("can't create client for service")) + defer decorate.OnError(&err, gotext.Get("can't create client for service")) conn, err := grpc.Dial(fmt.Sprintf("unix:%s", socket), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithStreamInterceptor(interceptorschain.StreamClient( diff --git a/internal/adsysservice/doc.go b/internal/adsysservice/doc.go index 1c60da2ec..d14a2c102 100644 --- a/internal/adsysservice/doc.go +++ b/internal/adsysservice/doc.go @@ -3,22 +3,23 @@ package adsysservice import ( "bufio" "embed" + "errors" "fmt" "path/filepath" "regexp" "strings" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/docs" "github.com/ubuntu/adsys/internal/authorizer" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" ) // GetDoc returns a chapter documentation from server. func (s *Service) GetDoc(r *adsys.GetDocRequest, stream adsys.Service_GetDocServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while getting documentation")) + defer decorate.OnError(&err, gotext.Get("error while getting documentation")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -27,7 +28,7 @@ func (s *Service) GetDoc(r *adsys.GetDocRequest, stream adsys.Service_GetDocServ // Get all documentation metadata _, chaptersToFiles, filesToTitle, err := docStructure(docs.Dir, "index.md", "") if err != nil { - return fmt.Errorf(i18n.G("could not list documentation directory: %v"), err) + return errors.New(gotext.Get("could not list documentation directory: %v", err)) } // Find a match, removing trailing / for directory folder. @@ -40,7 +41,7 @@ func (s *Service) GetDoc(r *adsys.GetDocRequest, stream adsys.Service_GetDocServ out, err := renderDocumentationPage(p, filesToTitle) if err != nil { - return fmt.Errorf(i18n.G("could not read chapter %q: %v"), chapter, err) + return errors.New(gotext.Get("could not read chapter %q: %v", chapter, err)) } if err := stream.Send(&adsys.StringResponse{ @@ -53,7 +54,7 @@ func (s *Service) GetDoc(r *adsys.GetDocRequest, stream adsys.Service_GetDocServ // ListDoc returns a list of all documentation from server. func (s *Service) ListDoc(_ *adsys.Empty, stream adsys.Service_ListDocServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while listing documentation")) + defer decorate.OnError(&err, gotext.Get("error while listing documentation")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -61,7 +62,7 @@ func (s *Service) ListDoc(_ *adsys.Empty, stream adsys.Service_ListDocServer) (e chapters, _, _, err := docStructure(docs.Dir, "index.md", "") if err != nil { - return fmt.Errorf(i18n.G("could not list documentation directory: %v"), err) + return errors.New(gotext.Get("could not list documentation directory: %v", err)) } if err := stream.Send(&adsys.ListDocReponse{ @@ -213,7 +214,7 @@ func renderDocumentationPage(p string, filesToTitle map[string]string) (string, f, err := docs.Dir.Open(p) if err != nil { - return "", fmt.Errorf(i18n.G("no file %q found in documentation"), p) + return "", errors.New(gotext.Get("no file %q found in documentation", p)) } defer f.Close() diff --git a/internal/adsysservice/policy.go b/internal/adsysservice/policy.go index f30e0ca46..6a5190b7c 100644 --- a/internal/adsysservice/policy.go +++ b/internal/adsysservice/policy.go @@ -4,12 +4,12 @@ import ( "context" "fmt" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/internal/ad" "github.com/ubuntu/adsys/internal/adsysservice/actions" "github.com/ubuntu/adsys/internal/authorizer" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies" "github.com/ubuntu/adsys/internal/policies/certificate" "github.com/ubuntu/decorate" @@ -19,7 +19,7 @@ import ( // UpdatePolicy refreshes or creates a policy for current user or user given as argument. // It can purge the policy instead of updating it if requested. func (s *Service) UpdatePolicy(r *adsys.UpdatePolicyRequest, stream adsys.Service_UpdatePolicyServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while updating policy")) + defer decorate.OnError(&err, gotext.Get("error while updating policy")) objectClass := ad.UserObject if r.GetIsComputer() || r.GetAll() { @@ -84,7 +84,7 @@ func (s *Service) updatePolicyFor(ctx context.Context, isComputer bool, target s // DumpPolicies displays all applied policies for a given user. func (s *Service) DumpPolicies(r *adsys.DumpPoliciesRequest, stream adsys.Service_DumpPoliciesServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while displaying applied policies")) + defer decorate.OnError(&err, gotext.Get("error while displaying applied policies")) objectClass := ad.UserObject if r.GetIsComputer() { @@ -119,7 +119,7 @@ func (s *Service) DumpPolicies(r *adsys.DumpPoliciesRequest, stream adsys.Servic // DumpPoliciesDefinitions dumps requested policy definitions stored in daemon at build time. func (s *Service) DumpPoliciesDefinitions(r *adsys.DumpPolicyDefinitionsRequest, stream adsys.Service_DumpPoliciesDefinitionsServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while dumping policy definitions")) + defer decorate.OnError(&err, gotext.Get("error while dumping policy definitions")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -142,7 +142,7 @@ func (s *Service) DumpPoliciesDefinitions(r *adsys.DumpPolicyDefinitionsRequest, // GPOListScript returns the embedded GPO python list script. func (s *Service) GPOListScript(_ *adsys.Empty, stream adsys.Service_GPOListScriptServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while getting gpo list script")) + defer decorate.OnError(&err, gotext.Get("error while getting gpo list script")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -159,7 +159,7 @@ func (s *Service) GPOListScript(_ *adsys.Empty, stream adsys.Service_GPOListScri // CertAutoEnrollScript returns the embedded certificate autoenrollment python script. func (s *Service) CertAutoEnrollScript(_ *adsys.Empty, stream adsys.Service_CertAutoEnrollScriptServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while getting certificate autoenrollment script")) + defer decorate.OnError(&err, gotext.Get("error while getting certificate autoenrollment script")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err diff --git a/internal/adsysservice/service.go b/internal/adsysservice/service.go index cebb5e1d0..3e13d32d3 100644 --- a/internal/adsysservice/service.go +++ b/internal/adsysservice/service.go @@ -8,12 +8,12 @@ import ( "time" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/internal/adsysservice/actions" "github.com/ubuntu/adsys/internal/authorizer" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies" "github.com/ubuntu/adsys/internal/stdforward" "github.com/ubuntu/decorate" @@ -24,7 +24,7 @@ import ( // Anything logged by the server on stdout, stderr or via the standard logger. // Only one call at a time can be performed here. func (s *Service) Cat(_ *adsys.Empty, stream adsys.Service_CatServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while trying to display daemon output")) + defer decorate.OnError(&err, gotext.Get("error while trying to display daemon output")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), actions.ActionServiceManage); err != nil { return err @@ -60,7 +60,7 @@ func (ss streamWriter) Write(b []byte) (n int, err error) { // Status returns internal daemon status to the client. func (s *Service) Status(_ *adsys.Empty, stream adsys.Service_StatusServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while getting daemon status")) + defer decorate.OnError(&err, gotext.Get("error while getting daemon status")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -82,8 +82,8 @@ func (s *Service) Status(_ *adsys.Empty, stream adsys.Service_StatusServer) (err state.apparmorDir = consts.DefaultApparmorDir } - timeout := i18n.G("unknown") - socket := i18n.G("unknown") + timeout := gotext.Get("unknown") + socket := gotext.Get("unknown") if s.daemon != nil { timeout = s.daemon.Timeout().String() sock := s.daemon.GetSocketAddr() @@ -96,47 +96,48 @@ func (s *Service) Status(_ *adsys.Empty, stream adsys.Service_StatusServer) (err timeLayout := "Mon Jan 2 15:04" - nextRefresh := i18n.G("unknown") + nextRefresh := gotext.Get("unknown") if next, err := s.nextRefreshTime(); err == nil { nextRefresh = next.Format(timeLayout) } else { log.Warning(stream.Context(), err) } - updateFmt := i18n.G("%s, updated on %s") - updateMachine := i18n.G("Machine, no gpo applied found") + // FIXME: gotext.Get needs to have the arguments parsed. + updateFmt := "%s" + gotext.Get(", updated on ") + "%s" + updateMachine := gotext.Get("Machine, no gpo applied found") t, err := s.policyManager.LastUpdateFor(stream.Context(), "", true) if err == nil { - updateMachine = fmt.Sprintf(updateFmt, i18n.G("Machine"), t.Format(timeLayout)) + updateMachine = fmt.Sprintf(updateFmt, gotext.Get("Machine"), t.Format(timeLayout)) } - updateUsers := fmt.Sprint(i18n.G("Can't get connected users")) + updateUsers := fmt.Sprint(gotext.Get("Can't get connected users")) users, err := s.adc.ListUsers(stream.Context(), true) if err == nil { - updateUsers = fmt.Sprint(i18n.G("Connected users:")) + updateUsers = fmt.Sprint(gotext.Get("Connected users:")) for _, u := range users { if t, err := s.policyManager.LastUpdateFor(stream.Context(), u, false); err == nil { updateUsers = updateUsers + "\n " + fmt.Sprintf(updateFmt, u, t.Format(timeLayout)) } else { - updateUsers = updateUsers + "\n " + fmt.Sprintf(i18n.G("%s, no gpo applied found"), u) + updateUsers = updateUsers + "\n " + gotext.Get("%s, no gpo applied found", u) } } if len(users) == 0 { - updateUsers = updateUsers + "\n " + i18n.G("None") + updateUsers = updateUsers + "\n " + gotext.Get("None") } } - ubuntuProStatus := i18n.G("Ubuntu Pro subscription is not active on this machine. Rules belonging to the following policy types will not be applied:\n") + ubuntuProStatus := gotext.Get("Ubuntu Pro subscription is not active on this machine. Rules belonging to the following policy types will not be applied:\n") proOnlyRules := slices.Clone(policies.ProOnlyRules) slices.Sort(proOnlyRules) ubuntuProStatus = ubuntuProStatus + " - " + strings.Join(proOnlyRules, "\n - ") subscriptionEnabled := s.policyManager.GetSubscriptionState(stream.Context()) if subscriptionEnabled { - ubuntuProStatus = i18n.G("Ubuntu Pro subscription active.") + ubuntuProStatus = gotext.Get("Ubuntu Pro subscription active.") } - status := fmt.Sprintf(i18n.G(`%s + status := gotext.Get(`%s %s Next Refresh: %s @@ -153,7 +154,7 @@ Daemon: Dconf path: %s Sudoers path: %s PolicyKit path: %s - Apparmor path: %s`), updateMachine, updateUsers, nextRefresh, + Apparmor path: %s`, updateMachine, updateUsers, nextRefresh, ubuntuProStatus, strings.Join(strings.Split(adInfo, "\n"), "\n "), timeout, socket, state.cacheDir, state.runDir, state.dconfDir, @@ -171,7 +172,7 @@ Daemon: // Stop requests to stop the service once all connections are done. Force will shut it down immediately and drop // existing connections. func (s *Service) Stop(r *adsys.StopRequest, stream adsys.Service_StopServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while trying to stop daemon")) + defer decorate.OnError(&err, gotext.Get("error while trying to stop daemon")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), actions.ActionServiceManage); err != nil { return err @@ -183,7 +184,7 @@ func (s *Service) Stop(r *adsys.StopRequest, stream adsys.Service_StopServer) (e // ListUsers returns the list of currently active users. func (s *Service) ListUsers(r *adsys.ListUsersRequest, stream adsys.Service_ListUsersServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while trying to get the list of active users")) + defer decorate.OnError(&err, gotext.Get("error while trying to get the list of active users")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err @@ -204,10 +205,10 @@ func (s *Service) ListUsers(r *adsys.ListUsersRequest, stream adsys.Service_List // nextRefreshTime returns next adsys schedule refresh call. func (s Service) nextRefreshTime() (next *time.Time, err error) { - defer decorate.OnError(&err, i18n.G("error while trying to determine next refresh time")) + defer decorate.OnError(&err, gotext.Get("error while trying to determine next refresh time")) if s.initSystemTime == nil { - return nil, errors.New(i18n.G("no boot system time found")) + return nil, errors.New(gotext.Get("no boot system time found")) } const unit = "adsys-gpo-refresh.timer" @@ -218,11 +219,11 @@ func (s Service) nextRefreshTime() (next *time.Time, err error) { strings.ReplaceAll(strings.ReplaceAll(unit, ".", "_2e"), "-", "_2d")))) val, err := timerUnit.GetProperty(fmt.Sprintf("%s.NextElapseUSecMonotonic", consts.SystemdDbusTimerInterface)) if err != nil { - return nil, fmt.Errorf(i18n.G("could not find %s unit on systemd bus: no GPO refresh scheduled? %v"), unit, err) + return nil, errors.New(gotext.Get("could not find %s unit on systemd bus: no GPO refresh scheduled? %v", unit, err)) } nextRaw, ok := val.Value().(uint64) if !ok { - return nil, fmt.Errorf(i18n.G("invalid next GPO refresh value: %v"), val.Value(), err) + return nil, errors.New(gotext.Get("invalid next GPO refresh value for %v: %v", val.Value(), err)) } nextRefresh := s.initSystemTime.Add(time.Duration(nextRaw) * time.Microsecond / time.Nanosecond) diff --git a/internal/adsysservice/version.go b/internal/adsysservice/version.go index 6e42256e3..aa6f85427 100644 --- a/internal/adsysservice/version.go +++ b/internal/adsysservice/version.go @@ -1,17 +1,17 @@ package adsysservice import ( + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys" "github.com/ubuntu/adsys/internal/authorizer" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" ) // Version returns version from server. func (s *Service) Version(_ *adsys.Empty, stream adsys.Service_VersionServer) (err error) { - defer decorate.OnError(&err, i18n.G("error while getting daemon version")) + defer decorate.OnError(&err, gotext.Get("error while getting daemon version")) if err := s.authorizer.IsAllowedFromContext(stream.Context(), authorizer.ActionAlwaysAllowed); err != nil { return err diff --git a/internal/authorizer/authorizer.go b/internal/authorizer/authorizer.go index 615e40a04..441c5b419 100644 --- a/internal/authorizer/authorizer.go +++ b/internal/authorizer/authorizer.go @@ -15,8 +15,8 @@ import ( "strings" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "google.golang.org/grpc/peer" ) @@ -53,7 +53,7 @@ func withRoot(root string) func(*Authorizer) { // New returns a new authorizer. func New(bus *dbus.Conn, options ...func(*Authorizer)) (auth *Authorizer, err error) { - defer decorate.OnError(&err, i18n.G("can't create new authorizer")) + defer decorate.OnError(&err, gotext.Get("can't create new authorizer")) authority := bus.Object("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority") @@ -108,17 +108,17 @@ type authResult struct { // IsAllowedFromContext returns nil if the user is allowed to perform an operation. // The pid and uid are extracted from peerCredsInfo grpc context. func (a Authorizer) IsAllowedFromContext(ctx context.Context, action Action) (err error) { - log.Debug(ctx, i18n.G("Check if grpc request peer is authorized")) + log.Debug(ctx, gotext.Get("Check if grpc request peer is authorized")) - defer decorate.OnError(&err, i18n.G("permission denied")) + defer decorate.OnError(&err, gotext.Get("permission denied")) p, ok := peer.FromContext(ctx) if !ok { - return errors.New(i18n.G("context request doesn't have grpc peer creds informations.")) + return errors.New(gotext.Get("context request doesn't have grpc peer creds informations.")) } pci, ok := p.AuthInfo.(peerCredsInfo) if !ok { - return errors.New(i18n.G("context request grpc peer creeds information is not a peerCredsInfo.")) + return errors.New(gotext.Get("context request grpc peer creeds information is not a peerCredsInfo.")) } // Is it an action needing user checking? @@ -126,15 +126,15 @@ func (a Authorizer) IsAllowedFromContext(ctx context.Context, action Action) (er if action.SelfID != "" { userName, ok := ctx.Value(OnUserKey).(string) if !ok { - return errors.New(i18n.G("request to act on user action should have a user name attached")) + return errors.New(gotext.Get("request to act on user action should have a user name attached")) } user, err := a.userLookup(userName) if err != nil { - return fmt.Errorf(i18n.G("couldn't retrieve user for %q: %v"), userName, err) + return errors.New(gotext.Get("couldn't retrieve user for %q: %v", userName, err)) } uid, err := strconv.Atoi(user.Uid) if err != nil { - return fmt.Errorf(i18n.G("couldn't convert %q to a valid uid for %q"), user.Uid, userName) + return errors.New(gotext.Get("couldn't convert %q to a valid uid for %q", user.Uid, userName)) } actionUID = uint32(uid) } @@ -147,10 +147,10 @@ func (a Authorizer) IsAllowedFromContext(ctx context.Context, action Action) (er // (self or others). func (a Authorizer) isAllowed(ctx context.Context, action Action, pid int32, uid uint32, actionUID uint32) error { if uid == 0 { - log.Debug(ctx, i18n.G("Authorized as being administrator")) + log.Debug(ctx, gotext.Get("Authorized as being administrator")) return nil } else if action == ActionAlwaysAllowed { - log.Debug(ctx, i18n.G("Any user always authorized")) + log.Debug(ctx, gotext.Get("Any user always authorized")) return nil } else if action.SelfID != "" { action.ID = action.OtherID @@ -161,7 +161,7 @@ func (a Authorizer) isAllowed(ctx context.Context, action Action, pid int32, uid f, err := os.Open(filepath.Join(a.root, fmt.Sprintf("proc/%d/stat", pid))) if err != nil { - return fmt.Errorf(i18n.G("couldn't open stat file for process: %v"), err) + return errors.New(gotext.Get("couldn't open stat file for process: %v", err)) } defer decorate.LogFuncOnErrorContext(ctx, f.Close) @@ -185,13 +185,13 @@ func (a Authorizer) isAllowed(ctx context.Context, action Action, pid int32, uid "org.freedesktop.PolicyKit1.Authority.CheckAuthorization", dbus.FlagAllowInteractiveAuthorization, subject, action.ID, details, checkAllowInteraction, "").Store(&result) if err != nil { - return fmt.Errorf(i18n.G("call to polkit failed: %v"), err) + return errors.New(gotext.Get("call to polkit failed: %v", err)) } - log.Debugf(ctx, i18n.G("Polkit call result, authorized: %t"), result.IsAuthorized) + log.Debug(ctx, gotext.Get("Polkit call result, authorized: %t", result.IsAuthorized)) if !result.IsAuthorized { - return errors.New(i18n.G("polkit denied access")) + return errors.New(gotext.Get("polkit denied access")) } return nil } @@ -202,7 +202,7 @@ func (a Authorizer) isAllowed(ctx context.Context, action Action, pid int32, uid // // https://cgit.freedesktop.org/polkit/tree/src/polkit/polkitunixprocess.c func getStartTimeFromReader(r io.Reader) (t uint64, err error) { - defer decorate.OnError(&err, i18n.G("can't determine start time of client process")) + defer decorate.OnError(&err, gotext.Get("can't determine start time of client process")) data, err := io.ReadAll(r) if err != nil { @@ -220,19 +220,19 @@ func getStartTimeFromReader(r io.Reader) (t uint64, err error) { // starttime field. idx := strings.IndexByte(contents, ')') if idx < 0 { - return 0, errors.New(i18n.G("parsing error: missing )")) + return 0, errors.New(gotext.Get("parsing error: missing )")) } idx += 2 // skip ") " if idx > len(contents) { - return 0, errors.New(i18n.G("parsing error: ) at the end")) + return 0, errors.New(gotext.Get("parsing error: ) at the end")) } tokens := strings.Split(contents[idx:], " ") if len(tokens) < 20 { - return 0, errors.New(i18n.G("parsing error: less fields than required")) + return 0, errors.New(gotext.Get("parsing error: less fields than required")) } v, err := strconv.ParseUint(tokens[19], 10, 64) if err != nil { - return 0, fmt.Errorf(i18n.G("parsing error: %v"), err) + return 0, errors.New(gotext.Get("parsing error: %v", err)) } return v, nil } diff --git a/internal/authorizer/servercreds.go b/internal/authorizer/servercreds.go index 67edc3b1b..c5e8ca89c 100644 --- a/internal/authorizer/servercreds.go +++ b/internal/authorizer/servercreds.go @@ -2,10 +2,11 @@ package authorizer import ( "context" + "errors" "fmt" "net" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/decorate" "golang.org/x/sys/unix" "google.golang.org/grpc" @@ -21,20 +22,20 @@ func WithUnixPeerCreds() grpc.ServerOption { type serverPeerCreds struct{} func (serverPeerCreds) ServerHandshake(conn net.Conn) (n net.Conn, c credentials.AuthInfo, err error) { - defer decorate.OnError(&err, i18n.G("server handshake failed")) + defer decorate.OnError(&err, gotext.Get("server handshake failed")) var cred *unix.Ucred // net.Conn is an interface. Expect only *net.UnixConn types uc, ok := conn.(*net.UnixConn) if !ok { - return conn, nil, fmt.Errorf(i18n.G("unexpected socket type")) + return conn, nil, fmt.Errorf(gotext.Get("unexpected socket type")) } // Fetches raw network connection from UnixConn raw, err := uc.SyscallConn() if err != nil { - return conn, nil, fmt.Errorf(i18n.G("error opening raw connection: %s"), err) + return conn, nil, errors.New(gotext.Get("error opening raw connection: %s", err)) } // The raw.Control() callback does not return an error directly. @@ -47,10 +48,10 @@ func (serverPeerCreds) ServerHandshake(conn net.Conn) (n net.Conn, c credentials unix.SO_PEERCRED) }) if err != nil { - return conn, nil, fmt.Errorf(i18n.G("GetsockoptUcred() error: %s"), err) + return conn, nil, errors.New(gotext.Get("GetsockoptUcred() error: %s", err)) } if err2 != nil { - return conn, nil, fmt.Errorf(i18n.G("Control() error: %s"), err2) + return conn, nil, errors.New(gotext.Get("Control() error: %s", err2)) } return conn, peerCredsInfo{uid: cred.Uid, pid: cred.Pid}, nil diff --git a/internal/cmdhandler/cmdhandler.go b/internal/cmdhandler/cmdhandler.go index cd94afa9f..1cf3044a2 100644 --- a/internal/cmdhandler/cmdhandler.go +++ b/internal/cmdhandler/cmdhandler.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" + "github.com/leonelquinteros/gotext" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" ) @@ -35,7 +35,7 @@ func NoValidArgs(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellC // README and manpage refers to them in each subsection (parents are differents, but only one is kept if we use the same object). func RegisterAlias(cmd, parent *cobra.Command) { alias := *cmd - t := fmt.Sprintf(i18n.G("Alias of %q"), cmd.CommandPath()) + t := gotext.Get("Alias of %q", cmd.CommandPath()) if alias.Long != "" { t = fmt.Sprintf("%s (%s)", alias.Long, t) } @@ -45,14 +45,14 @@ func RegisterAlias(cmd, parent *cobra.Command) { // InstallVerboseFlag adds the -v and -vv options and returns the reference to it. func InstallVerboseFlag(cmd *cobra.Command, viper *viper.Viper) *int { - r := cmd.PersistentFlags().CountP("verbose", "v", i18n.G("issue INFO (-v), DEBUG (-vv) or DEBUG with caller (-vvv) output")) + r := cmd.PersistentFlags().CountP("verbose", "v", gotext.Get("issue INFO (-v), DEBUG (-vv) or DEBUG with caller (-vvv) output")) decorate.LogOnError(viper.BindPFlag("verbose", cmd.PersistentFlags().Lookup("verbose"))) return r } // InstallSocketFlag adds the -s and --sockets options and returns the reference to it. func InstallSocketFlag(cmd *cobra.Command, viper *viper.Viper, defaultPath string) *string { - s := cmd.PersistentFlags().StringP("socket", "s", defaultPath, i18n.G("socket path to use between daemon and client. Can be overridden by systemd socket activation.")) + s := cmd.PersistentFlags().StringP("socket", "s", defaultPath, gotext.Get("socket path to use between daemon and client. Can be overridden by systemd socket activation.")) decorate.LogOnError(viper.BindPFlag("socket", cmd.PersistentFlags().Lookup("socket"))) return s } @@ -63,7 +63,7 @@ func InstallConfigFlag(cmd *cobra.Command, persistent bool) *string { if persistent { target = cmd.PersistentFlags() } - return target.StringP("config", "c", "", i18n.G("use a specific configuration file")) + return target.StringP("config", "c", "", gotext.Get("use a specific configuration file")) } // CalledCmd returns the actual command called by the user inferred from the arguments. diff --git a/internal/cmdhandler/suggest.go b/internal/cmdhandler/suggest.go index 220c93f63..2f2474dca 100644 --- a/internal/cmdhandler/suggest.go +++ b/internal/cmdhandler/suggest.go @@ -4,14 +4,15 @@ import ( "fmt" "strings" + "github.com/leonelquinteros/gotext" "github.com/spf13/cobra" - "github.com/ubuntu/adsys/internal/i18n" ) // SubcommandsRequiredWithSuggestions will ensure we have a subcommand provided by the user and augments it with // suggestion for commands, alias and help on root command. func SubcommandsRequiredWithSuggestions(cmd *cobra.Command, args []string) error { - requireMsg := i18n.G("%s requires a valid subcommand") + // FIXME: bypass Get not supported only string without the command itself unfortunately + requireMsg := "%s " + gotext.Get("requires a valid subcommand") // This will be triggered if cobra didn't find any subcommands. // Find some suggestions. var suggestions []string @@ -48,14 +49,14 @@ func SubcommandsRequiredWithSuggestions(cmd *cobra.Command, args []string) error var suggestionsMsg string if len(suggestions) > 0 { - suggestionsMsg += i18n.G("Did you mean this?\n") + suggestionsMsg += gotext.Get("Did you mean this?\n") for _, s := range suggestions { - suggestionsMsg += fmt.Sprintf(i18n.G("\t%v\n"), s) + suggestionsMsg += gotext.Get("\t%v\n", s) } } if suggestionsMsg != "" { - requireMsg = fmt.Sprintf(i18n.G("%s. %s"), requireMsg, suggestionsMsg) + requireMsg = gotext.Get("%s. %s", requireMsg, suggestionsMsg) } return fmt.Errorf(requireMsg, cmd.Name()) diff --git a/internal/config/config.go b/internal/config/config.go index 0eeed0c6e..5f7f80891 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,12 +9,12 @@ import ( "path/filepath" "github.com/fsnotify/fsnotify" + "github.com/leonelquinteros/gotext" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" ) @@ -40,7 +40,7 @@ func SetVerboseMode(level int) { // to let you deserialize the initial configuration and returns any errors. // Then, it automatically watches any configuration changes and will call configChanged with refresh set to true. func Init(name string, cmd cobra.Command, vip *viper.Viper, configChanged func(refreshed bool) error) (err error) { - defer decorate.OnError(&err, i18n.G("can't load configuration")) + defer decorate.OnError(&err, gotext.Get("can't load configuration")) // Force a visit of the local flags so persistent flags for all parents are merged. cmd.LocalFlags() @@ -62,7 +62,7 @@ func Init(name string, cmd cobra.Command, vip *viper.Viper, configChanged func(r vip.AddConfigPath("/etc/") // Add the executable path to the config search path. if binPath, err := os.Executable(); err != nil { - log.Warningf(context.Background(), i18n.G("Failed to get current executable path, not adding it as a config dir: %v"), err) + log.Warning(context.Background(), gotext.Get("Failed to get current executable path, not adding it as a config dir: %v", err)) } else { vip.AddConfigPath(filepath.Dir(binPath)) } diff --git a/internal/config/watchd/watchd.go b/internal/config/watchd/watchd.go index 2c019dfcc..5b265227c 100644 --- a/internal/config/watchd/watchd.go +++ b/internal/config/watchd/watchd.go @@ -9,8 +9,8 @@ import ( "path/filepath" "strings" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "gopkg.in/yaml.v3" ) @@ -31,12 +31,12 @@ func DirsFromConfigFile(ctx context.Context, configFile string) []string { var dirs []string config, err := os.ReadFile(configFile) if err != nil { - log.Debugf(ctx, i18n.G("Could not read config file: %v"), err) + log.Debug(ctx, gotext.Get("Could not read config file: %v", err)) return dirs } cfg := AppConfig{} if err := yaml.Unmarshal(config, &cfg); err != nil { - log.Debugf(ctx, i18n.G("Could not unmarshal config YAML: %v"), err) + log.Debug(ctx, gotext.Get("Could not unmarshal config YAML: %v", err)) return dirs } dirs = cfg.Dirs @@ -48,28 +48,28 @@ func DirsFromConfigFile(ctx context.Context, configFile string) []string { // directories that are passed in actually exist. It receives a config file and // a slice of absolute sorted paths. func WriteConfig(confFile string, dirs []string) (err error) { - defer decorate.OnError(&err, i18n.G("can't write config")) + defer decorate.OnError(&err, gotext.Get("can't write config")) if len(dirs) == 0 { - return fmt.Errorf(i18n.G("needs at least one directory to watch")) + return errors.New(gotext.Get("needs at least one directory to watch")) } // Make sure all directories exist for _, dir := range dirs { if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { - return fmt.Errorf(i18n.G("directory %q does not exist"), dir) + return errors.New(gotext.Get("directory %q does not exist", dir)) } } // Make sure the directory structure exists for the config file if err := os.MkdirAll(filepath.Dir(confFile), 0750); err != nil { - return fmt.Errorf(i18n.G("unable to create config directory: %v"), err) + return errors.New(gotext.Get("unable to create config directory: %v", err)) } cfg := AppConfig{Dirs: dirs, Verbose: 0} data, err := yaml.Marshal(&cfg) if err != nil { - return fmt.Errorf(i18n.G("unable to marshal: %v"), err) + return errors.New(gotext.Get("unable to marshal: %v", err)) } if err := os.WriteFile(confFile, data, 0600); err != nil { @@ -86,7 +86,7 @@ func WriteConfig(confFile string, dirs []string) (err error) { // only covers the cases used by the service installer, which should be good // enough for us. func ConfigFileFromArgs(args string) (string, error) { - err := fmt.Errorf(i18n.G("missing config file in CLI arguments")) + err := fmt.Errorf(gotext.Get("missing config file in CLI arguments")) _, configFile, found := strings.Cut(args, "-c") if !found { @@ -107,7 +107,7 @@ func ConfigFileFromArgs(args string) (string, error) { func DefaultConfigPath() string { binPath, err := os.Executable() if err != nil { - log.Warningf(context.Background(), i18n.G("failed to get executable path, using relative path for default config: %v"), err) + log.Warning(context.Background(), gotext.Get("failed to get executable path, using relative path for default config: %v", err)) } return filepath.Join(filepath.Dir(binPath), fmt.Sprintf("%s.yaml", CmdName)) } diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index eaa6ce7cd..144a5a822 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -4,6 +4,7 @@ package daemon import ( "context" + "errors" "fmt" "net" "os" @@ -12,8 +13,8 @@ import ( "github.com/coreos/go-systemd/v22/activation" "github.com/coreos/go-systemd/v22/daemon" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "google.golang.org/grpc" ) @@ -69,7 +70,7 @@ func WithServerQuit(f func(context.Context)) func(o *options) error { // New returns an new, initialized daemon server, which handles systemd activation. // If systemd activation is used, it will override any socket passed here. func New(registerGRPCServer GRPCServerRegisterer, socket string, opts ...option) (d *Daemon, err error) { - defer decorate.OnError(&err, i18n.G("can't create daemon")) + defer decorate.OnError(&err, gotext.Get("can't create daemon")) // defaults args := options{ @@ -110,7 +111,7 @@ func New(registerGRPCServer GRPCServerRegisterer, socket string, opts ...option) d.useSocketActivation = true d.lis <- listeners[0] default: - return nil, fmt.Errorf(i18n.G("unexpected number of systemd socket activation (%d != 1)"), len(listeners)) + return nil, errors.New(gotext.Get("unexpected number of systemd socket activation (%d != 1)", len(listeners))) } d.grpcserver = d.registerGRPCServer(d) @@ -128,7 +129,7 @@ func (d *Daemon) UseSocket(socket string) (err error) { return nil } - defer decorate.OnError(&err, i18n.G("can't listen on new socket %q"), socket) + defer decorate.OnError(&err, gotext.Get("can't listen on new socket %q", socket)) lis, err := net.Listen("unix", socket) if err != nil { @@ -155,12 +156,12 @@ func (d *Daemon) UseSocket(socket string) (err error) { // When the server stop listening, the socket is removed automatically. // Configuration can be reloaded and we will then listen on the new socket. func (d *Daemon) Listen() (err error) { - defer decorate.OnError(&err, i18n.G("can't serve")) + defer decorate.OnError(&err, gotext.Get("can't serve")) if sent, err := d.systemdSdNotifier(false, "READY=1"); err != nil { - return fmt.Errorf(i18n.G("couldn't send ready notification to systemd: %v"), err) + return errors.New(gotext.Get("couldn't send ready notification to systemd: %v", err)) } else if sent { - log.Debug(context.Background(), i18n.G("Ready state sent to systemd")) + log.Debug(context.Background(), gotext.Get("Ready state sent to systemd")) } lis := <-d.lis @@ -170,7 +171,7 @@ func (d *Daemon) Listen() (err error) { // handle socket configuration reloading for { - log.Infof(context.Background(), i18n.G("Serving on %s"), lis.Addr().String()) + log.Info(context.Background(), gotext.Get("Serving on %s", lis.Addr().String())) if err := (d.grpcserver.Serve(lis)); err != nil { return fmt.Errorf("unable to start GRPC server: %w", err) } @@ -188,7 +189,7 @@ func (d *Daemon) Listen() (err error) { d.socketMu.Unlock() d.grpcserver = d.registerGRPCServer(d) } - log.Debug(context.Background(), i18n.G("Quitting")) + log.Debug(context.Background(), gotext.Get("Quitting")) d.serverQuit(context.Background()) return nil @@ -210,15 +211,15 @@ func (d *Daemon) Quit(force bool) { // stop gracefully stops the grpc server unless force is true. func (d *Daemon) stop(force bool) { - log.Info(context.Background(), i18n.G("Stopping daemon requested.")) + log.Info(context.Background(), gotext.Get("Stopping daemon requested.")) if force { d.grpcserver.Stop() return } - log.Info(context.Background(), i18n.G("Wait for active requests to close.")) + log.Info(context.Background(), gotext.Get("Wait for active requests to close.")) d.grpcserver.GracefulStop() - log.Debug(context.Background(), i18n.G("All connections have now ended.")) + log.Debug(context.Background(), gotext.Get("All connections have now ended.")) } // GetSocketAddr returns currently used socket address by daemon. diff --git a/internal/grpc/grpcerror/grpcerror.go b/internal/grpc/grpcerror/grpcerror.go index 3a8c0cbc1..eb7e3af2c 100644 --- a/internal/grpc/grpcerror/grpcerror.go +++ b/internal/grpc/grpcerror/grpcerror.go @@ -3,9 +3,8 @@ package grpcerror import ( "errors" - "fmt" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -26,16 +25,16 @@ func Format(err error, daemonName string) error { switch st.Code() { // no daemon case codes.Unavailable: - err = fmt.Errorf(i18n.G("Couldn't connect to %s daemon: %v"), daemonName, st.Message()) + err = errors.New(gotext.Get("Couldn't connect to %s daemon: %v", daemonName, st.Message())) // timeout case codes.DeadlineExceeded: - err = errors.New(i18n.G("Service took too long to respond. Disconnecting client.")) + err = errors.New(gotext.Get("Service took too long to respond. Disconnecting client.")) // regular error without annotation case codes.Unknown: - err = fmt.Errorf(i18n.G("Error from server: %v"), st.Message()) + err = errors.New(gotext.Get("Error from server: %v", st.Message())) // grpc error, just format it default: - err = fmt.Errorf(i18n.G("Error %s from server: %v"), st.Code(), st.Message()) + err = errors.New(gotext.Get("Error %s from server: %v", st.Code(), st.Message())) } return err } diff --git a/internal/grpc/logstreamer/log.go b/internal/grpc/logstreamer/log.go index 95e868fbc..8df30245a 100644 --- a/internal/grpc/logstreamer/log.go +++ b/internal/grpc/logstreamer/log.go @@ -9,12 +9,13 @@ package log import ( "context" + "errors" "fmt" "strings" "sync" + "github.com/leonelquinteros/gotext" "github.com/sirupsen/logrus" - "github.com/ubuntu/adsys/internal/i18n" ) const ( @@ -173,7 +174,7 @@ func log(ctx context.Context, level logrus.Level, args ...interface{}) { } if err := logLocallyMaybeRemote(level, caller, msg, localLogger, idRequest, sendStream); err != nil { - localLogger.Warningf(localLogFormatWithID, idRequest, i18n.G("couldn't send logs to client")) + localLogger.Warningf(localLogFormatWithID, idRequest, gotext.Get("couldn't send logs to client")) } } @@ -181,7 +182,7 @@ func logLocallyMaybeRemote(level logrus.Level, caller, msg string, localLogger * // decorate depends on logstreamer: we can’t use it here defer func() { if err != nil { - err = fmt.Errorf(i18n.G("can't send logs to client: %v"), err) + err = errors.New(gotext.Get("can't send logs to client: %v", err)) } }() diff --git a/internal/grpc/logstreamer/server.go b/internal/grpc/logstreamer/server.go index 340537c00..d4447324e 100644 --- a/internal/grpc/logstreamer/server.go +++ b/internal/grpc/logstreamer/server.go @@ -8,8 +8,8 @@ import ( "math/big" "strconv" + "github.com/leonelquinteros/gotext" "github.com/sirupsen/logrus" - "github.com/ubuntu/adsys/internal/i18n" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) @@ -40,10 +40,10 @@ func StreamServerInterceptor(localLogger *logrus.Logger) func(srv interface{}, s // create and log request ID idRequest := fmt.Sprintf("%s:%s", clientID, createID()) - if err := ssLogs.sendLogs(logrus.DebugLevel.String(), "", fmt.Sprintf(i18n.G("Connecting as [[%s]]"), idRequest)); err != nil { - localLogger.Warningf(localLogFormatWithID, idRequest, i18n.G("Couldn't send initial connection log to client")) + if err := ssLogs.sendLogs(logrus.DebugLevel.String(), "", gotext.Get("Connecting as [[%s]]", idRequest)); err != nil { + localLogger.Warningf(localLogFormatWithID, idRequest, gotext.Get("Couldn't send initial connection log to client")) } - Infof(context.Background(), i18n.G("New connection from client [[%s]]"), idRequest) + Info(context.Background(), gotext.Get("New connection from client [[%s]]", idRequest)) // attach stream logger options to context so that we can log locally and remotely from context ssLogs.ctx = context.WithValue(ss.Context(), logContextKey, logContext{ @@ -85,14 +85,14 @@ func extractMetaFromContext(ctx context.Context) (clientID string, withCaller bo // decorate depends on logstreamer: we can’t use it here defer func() { if err != nil { - err = fmt.Errorf(i18n.G("invalid metdata from client: %v\n. Please use the StreamClientInterceptor: %v"), err) + err = errors.New(gotext.Get("invalid metdata from client: %v\n. Please use the StreamClientInterceptor: %v", clientID, err)) } }() // extract logs metadata from the client md, ok := metadata.FromIncomingContext(ctx) if !ok { - return "", false, errors.New(i18n.G("missing client metadata")) + return "", false, errors.New(gotext.Get("missing client metadata")) } clientID, err = validUniqueMdEntry(md, clientIDKey) if err != nil { @@ -104,7 +104,7 @@ func extractMetaFromContext(ctx context.Context) (clientID string, withCaller bo } withCaller, err = strconv.ParseBool(withCallerRaw) if err != nil { - return "", false, fmt.Errorf(i18n.G("%s isn't a boolean: %v"), clientWantCallerKey, err) + return "", false, errors.New(gotext.Get("%s isn't a boolean: %v", clientWantCallerKey, err)) } return clientID, withCaller, nil @@ -113,10 +113,10 @@ func extractMetaFromContext(ctx context.Context) (clientID string, withCaller bo func validUniqueMdEntry(md metadata.MD, key string) (string, error) { v := md.Get(key) if len(v) == 0 { - return "", fmt.Errorf(i18n.G("missing metadata %s for incoming request"), key) + return "", errors.New(gotext.Get("missing metadata %s for incoming request", key)) } if len(v) != 1 { - return "", fmt.Errorf(i18n.G("invalid metadata %s for incoming request: %q"), key, v) + return "", errors.New(gotext.Get("invalid metadata %s for incoming request: %q", key, v)) } return v[0], nil } diff --git a/internal/i18n/export_test.go b/internal/i18n/export_test.go deleted file mode 100644 index c70b8f9c9..000000000 --- a/internal/i18n/export_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package i18n - -// WithLocaleDir enables overriding locale directory in tests. -func WithLocaleDir(path string) func(l *i18n) { - return func(l *i18n) { - l.localeDir = path - } -} - -// WithLoc enables overriding loc settings in tests. -func WithLoc(loc string) func(l *i18n) { - return func(l *i18n) { - l.loc = loc - } -} - -// ResetGlobals resets G and GN to their empty func. -func ResetGlobals() { - G = func(msgid string) string { return msgid } - NG = func(msgid string, msgidPlural string, n uint32) string { return msgid } -} diff --git a/internal/i18n/generate-locales.go b/internal/i18n/generate-locales.go deleted file mode 100644 index 1fa98dfc4..000000000 --- a/internal/i18n/generate-locales.go +++ /dev/null @@ -1,256 +0,0 @@ -//go:build tools -// +build tools - -package main - -import ( - "bufio" - "fmt" - "io/fs" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/ubuntu/adsys/internal/generators" -) - -const usage = `Usage of %s: - - create-po POT DIRECTORY LOC [LOC...] - Create new LOC.po file(s) in DIRECTORY from an existing pot file. - update-po POT DIRECTORY - Create/Update a pot file and refresh any existing po files in DIRECTORY. - generate-mo DOMAIN PODIR DIRECTORY - Create .mo files for any .po in PODIR in an structured hierarchy in DIRECTORY. -` - -func main() { - if len(os.Args) < 2 { - log.Fatalf(usage, os.Args[0]) - } - - switch os.Args[1] { - case "create-po": - if len(os.Args) < 5 { - log.Fatalf(usage, os.Args[0]) - } - if generators.InstallOnlyMode() { - return - } - if err := createPo(os.Args[2], os.Args[3], os.Args[4:]); err != nil { - log.Fatalf("Error when creating po files: %v", err) - } - - case "update-po": - if len(os.Args) != 4 { - log.Fatalf(usage, os.Args[0]) - } - if generators.InstallOnlyMode() { - return - } - if err := updatePo(os.Args[2], os.Args[3]); err != nil { - log.Fatalf("Error when updating po files: %v", err) - } - - case "generate-mo": - if len(os.Args) != 5 { - log.Fatalf(usage, os.Args[0]) - } - if err := generateMo(os.Args[2], os.Args[3], filepath.Join(generators.DestDirectory(os.Args[4]), "usr", "share")); err != nil { - log.Fatalf("Error when generating mo files: %v", err) - } - default: - log.Fatalf(usage, os.Args[0]) - } -} - -// createPo creates new po files. -func createPo(potfile, localeDir string, locs []string) error { - if _, err := os.Stat(potfile); err != nil { - return fmt.Errorf("%q can't be read: %v", potfile, err) - } - - for _, loc := range locs { - pofile := filepath.Join(localeDir, loc+".po") - if _, err := os.Stat(pofile); err == nil { - log.Printf("Skipping %q as already exists. Please use update-po to refresh it or delete it first.", loc) - continue - } - - if out, err := exec.Command("msginit", - "--input="+potfile, "--locale="+loc+".UTF-8", "--no-translator", "--output="+pofile).CombinedOutput(); err != nil { - return fmt.Errorf("couldn't create %q: %v.\nCommand output: %s", pofile, err, out) - } - } - - return nil -} - -// updatePo creates pot files and update any existing .po ones. -func updatePo(potfile, localeDir string) error { - if err := os.MkdirAll(localeDir, 0755); err != nil { - return fmt.Errorf("couldn't create directory for %q: %v", localeDir, err) - } - - // Create pot file - var files []string - root := filepath.Dir(localeDir) - err := filepath.WalkDir(root, func(p string, de fs.DirEntry, err error) error { - if err != nil { - return fmt.Errorf("fail to access %q: %v", p, err) - } - // Only deal with files - if de.IsDir() { - return nil - } - - if !strings.HasSuffix(p, ".go") && !strings.HasSuffix(p, ".go.template") { - return nil - } - files = append(files, strings.TrimPrefix(p, root+"/")) - - return nil - }) - - if err != nil { - return err - } - - var potcreation string - // if already existed: extract POT creation date to keep it (xgettext always updates it) - if _, err := os.Stat(potfile); err == nil { - if potcreation, err = getPOTCreationDate(potfile); err != nil { - log.Fatal(err) - } - } - args := append([]string{ - "--keyword=G", "--keyword=GN", "--add-comments", "--sort-output", "--package-name=" + strings.TrimSuffix(filepath.Base(potfile), ".pot"), - "-D", root, "--output=" + potfile}, files...) - if out, err := exec.Command("xgettext", args...).CombinedOutput(); err != nil { - return fmt.Errorf("couldn't compile pot file: %v\nCommand output: %s", err, out) - } - if potcreation != "" { - if err := rewritePOTCreationDate(potcreation, potfile); err != nil { - log.Fatalf("couldn't change POT Creation file: %v", err) - } - } - - // Merge existing po files - poCandidates, err := os.ReadDir(localeDir) - if err != nil { - log.Fatalf("couldn't list content of %q: %v", localeDir, err) - } - for _, f := range poCandidates { - if !strings.HasSuffix(f.Name(), ".po") { - continue - } - - pofile := filepath.Join(localeDir, f.Name()) - - // extract POT creation date to keep it (msgmerge always updates it) - potcreation, err := getPOTCreationDate(pofile) - if err != nil { - log.Fatal(err) - } - - if out, err := exec.Command("msgmerge", "--update", "--backup=none", pofile, potfile).CombinedOutput(); err != nil { - return fmt.Errorf("couldn't refresh %q: %v.\nCommand output: %s", pofile, err, out) - } - - if err := rewritePOTCreationDate(potcreation, pofile); err != nil { - log.Fatalf("couldn't change POT Creation file: %v", err) - } - } - - return nil -} - -// generateMo generates a locale directory structure with a mo for each po in localeDir. -func generateMo(domain, in, out string) error { - baseLocaleDir := filepath.Join(out, "locale") - if err := generators.CleanDirectory(baseLocaleDir); err != nil { - log.Fatalln(err) - } - - poCandidates, err := os.ReadDir(in) - if err != nil { - log.Fatalf("couldn't list content of %q: %v", in, err) - } - for _, f := range poCandidates { - if !strings.HasSuffix(f.Name(), ".po") { - continue - } - - candidate := filepath.Join(in, f.Name()) - outDir := filepath.Join(baseLocaleDir, strings.TrimSuffix(f.Name(), ".po"), "LC_MESSAGES") - if err := os.MkdirAll(outDir, 0755); err != nil { - return fmt.Errorf("couldn't create %q: %v", out, err) - } - if out, err := exec.Command("msgfmt", - "--output-file="+filepath.Join(outDir, domain+".mo"), - candidate).CombinedOutput(); err != nil { - return fmt.Errorf("couldn't compile mo file from %q: %v.\nCommand output: %s", candidate, err, out) - } - } - return nil -} - -const potCreationDatePrefix = `"POT-Creation-Date:` - -func getPOTCreationDate(p string) (string, error) { - f, err := os.Open(p) - if err != nil { - return "", fmt.Errorf("couldn't open %q: %v", p, err) - } - defer f.Close() - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - if strings.HasPrefix(scanner.Text(), potCreationDatePrefix) { - return scanner.Text(), nil - } - } - - if err := scanner.Err(); err != nil { - return "", fmt.Errorf("error while reading %q: %v", p, err) - } - - return "", fmt.Errorf("didn't find %q in %q", potCreationDatePrefix, p) -} - -func rewritePOTCreationDate(potcreation, p string) error { - f, err := os.Open(p) - if err != nil { - return fmt.Errorf("couldn't open %q: %v", p, err) - } - defer f.Close() - out, err := os.Create(p + ".new") - if err != nil { - return fmt.Errorf("couldn't open %q: %v", p+".new", err) - } - defer out.Close() - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - t := scanner.Text() - if strings.HasPrefix(t, potCreationDatePrefix) { - t = potcreation - } - if _, err := out.WriteString(t + "\n"); err != nil { - return fmt.Errorf("couldn't write to %q: %v", p+".new", err) - } - } - - if err := scanner.Err(); err != nil { - return fmt.Errorf("error while reading %q: %v", p, err) - } - f.Close() - out.Close() - - if err := os.Rename(p+".new", p); err != nil { - return fmt.Errorf("couldn't rename %q to %q: %v", p+".new", p, err) - } - return nil -} diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go deleted file mode 100644 index da745d3b6..000000000 --- a/internal/i18n/i18n.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package i18n is responsible for internationalization/translation handling and generation. -package i18n - -/* - * This package is inspired from https://github.com/snapcore/snapd/blob/master/i18n, with other snap dependecies removed - * and adapted to follow common go best practices. - */ - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/snapcore/go-gettext" -) - -type i18n struct { - domain string - localeDir string - loc string - - gettext.Catalog - translations gettext.Translations -} - -var ( - locale i18n - - // G is the shorthand for Gettext. - G = func(msgid string) string { return msgid } - // NG is the shorthand for NGettext. - NG = func(msgid string, msgidPlural string, n uint32) string { return msgid } -) - -// InitI18nDomain calls bind + set locale to system values. -func InitI18nDomain(domain string, options ...func(l *i18n)) { - locale = i18n{ - domain: domain, - localeDir: "/usr/share/locale", - } - for _, option := range options { - option(&locale) - } - - locale.bindTextDomain(locale.domain, locale.localeDir) - locale.setLocale(locale.loc) - - G = locale.Gettext - NG = locale.NGettext -} - -// langpackResolver tries to fetch locale mo file path. -// It first checks for the real locale (e.g. de_DE) and then -// tries to simplify the locale (e.g. de_DE -> de). -func langpackResolver(root string, locale string, domain string) string { - for _, locale := range []string{locale, strings.SplitN(locale, "_", 2)[0]} { - r := filepath.Join(locale, "LC_MESSAGES", fmt.Sprintf("%s.mo", domain)) - - // look into the generated mo files path first for translations, then the system - var candidateDirs []string - // Ubuntu uses /usr/share/locale-langpack and patches the glibc gettext implementation - candidateDirs = append(candidateDirs, filepath.Join(root, "..", "locale-langpack")) - candidateDirs = append(candidateDirs, root) - - for _, dir := range candidateDirs { - candidateMo := filepath.Join(dir, r) - // Only load valid candidates, if we can't access it or have perm issues, ignore - if _, err := os.Stat(candidateMo); err != nil { - continue - } - return candidateMo - } - } - - return "" -} - -func (l *i18n) bindTextDomain(domain, dir string) { - l.translations = gettext.NewTranslations(dir, domain, langpackResolver) -} - -// setLocale initializes the locale name and simplify it. -// If empty, it defaults to system ones set in LC_MESSAGES and LANG. -func (l *i18n) setLocale(loc string) { - if loc == "" { - loc = os.Getenv("LC_MESSAGES") - if loc == "" { - loc = os.Getenv("LANG") - } - } - // de_DE.UTF-8, de_DE@euro all need to get simplified - loc = strings.Split(loc, "@")[0] - loc = strings.Split(loc, ".")[0] - - l.Catalog = l.translations.Locale(loc) -} diff --git a/internal/i18n/i18n_test.go b/internal/i18n/i18n_test.go deleted file mode 100644 index 94239bda6..000000000 --- a/internal/i18n/i18n_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package i18n_test - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/ubuntu/adsys/internal/i18n" -) - -const ( - defaultDomain = "adsys-test" - defaultLoc = "en_DK" - secondaryLoc = "en" -) - -var ( - defaultPo = fmt.Sprintf(` -msgid "" -msgstr "" -"Project-Id-Version: %s\n" -"Report-Msgid-Bugs-To: adsys-devel@lists.ubuntu.com\n" -"POT-Creation-Date: 2013-10-05 14:08+0200\n" -"Language: %s\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;>\n" -msgid "plural_1" -msgid_plural "plural_2" -msgstr[0] "translated plural_1" -msgstr[1] "translated plural_2" -msgid "singular" -msgstr "translated singular" -`, defaultDomain, defaultLoc) - - localePo = map[string]string{ - defaultLoc: defaultPo, - secondaryLoc: strings.ReplaceAll( - strings.ReplaceAll(defaultPo, defaultLoc, secondaryLoc), - "translated singular", "secondary translated singular"), - } -) - -func TestTranslations(t *testing.T) { - t.Skip("WILL BE REMOVED") - defaultLocaleDir := filepath.Join(t.TempDir(), "locale") - compileMoFiles(t, defaultLocaleDir) - - tests := map[string]struct { - // default is singular/translated singular - text []string - want string - - localeDir string - lcmessages string // lcmessages can be set to "-" to ensure it's empty - lang string - domain string - loc string // loc can be set to "-" to ensure it's empty - - rename map[string]string - noinit bool - }{ - "One text elem, prefer en_DK over en": {}, - "Multiple text elems": {text: []string{"plural_1", "plural_2"}, want: "translated plural_1"}, - - // Locale preferences - "en_DK@ is en_DK": {loc: defaultLoc + "@foo"}, - "en_DK. is en_DK": {loc: defaultLoc + ".foo"}, - "Fallback to en if en_DK isn't present": { - want: "secondary translated singular", - rename: map[string]string{filepath.Join(defaultLocaleDir, "en_DK"): filepath.Join(defaultLocaleDir, "other")}, - }, - "Prefer locale-langpack to locale": { - want: "secondary translated singular", - rename: map[string]string{ - filepath.Join(defaultLocaleDir, "en"): filepath.Join(strings.ReplaceAll(defaultLocaleDir, "locale", "locale-langpack"), "en_DK"), - }, - }, - - "No loc prefers LC_MESSAGES first": {lcmessages: "en_DK", loc: "-"}, - "No loc fallbacks to LANG if no LC_MESSAGES": {lang: "en_DK", loc: "-", lcmessages: "-"}, - - "Untranslated elem": {text: []string{"untranslated"}, want: "untranslated"}, - "Missing locale": {loc: "doesntexists", want: "singular"}, - "Missing domain": {domain: "doesntexists", want: "singular"}, - "Invalid locale directory": {localeDir: "/doesntexists", want: "singular"}, - "Init wasn't ran": {noinit: true, want: "singular"}, - } - - for name, tc := range tests { - tc := tc - t.Run(name, func(t *testing.T) { - // We can't run those subtests in parallel as we want defer of global functions to end once all subtests are. - - // As we can't run those tests in parallel, we are doing the file switches and env changes to test priorities - // in subtests here and reset globals. - defer i18n.ResetGlobals() - if tc.text == nil { - tc.text = []string{"singular"} - } - if tc.want == "" { - tc.want = "translated singular" - } - if tc.localeDir == "" { - tc.localeDir = defaultLocaleDir - } - if tc.lcmessages == "" { - tc.lcmessages = "FR_fr" - } else if tc.lcmessages == "-" { - tc.lcmessages = "" - } - t.Setenv("LC_MESSAGES", tc.lcmessages) - if tc.lang == "" { - tc.lang = "FR_fr" - } - t.Setenv("LANG", tc.lang) - if tc.loc == "" { - tc.loc = defaultLoc - } else if tc.loc == "-" { - tc.loc = "" - } - if tc.domain == "" { - tc.domain = defaultDomain - } - if tc.rename != nil { - for old, new := range tc.rename { - renameElem(t, old, new) - } - } - - if !tc.noinit { - i18n.InitI18nDomain(tc.domain, i18n.WithLocaleDir(tc.localeDir), i18n.WithLoc(tc.loc)) - } - switch len(tc.text) { - case 1: - assert.Equal(t, tc.want, i18n.G(tc.text[0])) - case 2: - assert.Equal(t, tc.want, i18n.NG(tc.text[0], tc.text[1], 1)) - default: - t.Fatalf("unexpected case: %v", tc.text) - } - }) - } -} - -func compileMoFiles(t *testing.T, localeDir string) { - t.Helper() - - for loc, poContent := range localePo { - fullLocaleDir := filepath.Join(localeDir, loc, "LC_MESSAGES") - if err := os.MkdirAll(fullLocaleDir, 0750); err != nil { - t.Fatalf("couldn't create temporary directory %q: %v", fullLocaleDir, err) - } - - po := filepath.Join(localeDir, defaultDomain+".po") - mo := filepath.Join(fullLocaleDir, defaultDomain+".mo") - - if err := os.WriteFile(po, []byte(poContent), 0600); err != nil { - t.Fatalf("couldn't write po file: %v", err) - } - - // nolint:gosec // G204 false positive, the binary is hardcoded - cmd := exec.Command("msgfmt", po, "--output-file", mo) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - t.Fatalf("couldn't compile %q to %q: %v", po, mo, err) - } - } -} - -// renameElem rename old file to new. -// The rename is reverted when the test ends. -func renameElem(t *testing.T, old, new string) { - t.Helper() - - if err := os.MkdirAll(filepath.Dir(new), 0750); err != nil { - t.Fatalf("couldn't create parent directory %q to be renamed: %v", new, err) - } - if err := os.Rename(old, new); err != nil { - t.Fatalf("couldn't rename %q to %q: %v", old, new, err) - } - t.Cleanup(func() { - if err := os.Rename(new, old); err != nil { - t.Fatalf("couldn't restore %q to %q: %v", new, old, err) - } - }) -} diff --git a/internal/policies/apparmor/apparmor.go b/internal/policies/apparmor/apparmor.go index 48bf50911..3d47e3884 100644 --- a/internal/policies/apparmor/apparmor.go +++ b/internal/policies/apparmor/apparmor.go @@ -34,9 +34,9 @@ import ( "sync" "time" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/adsys/internal/smbsafe" "github.com/ubuntu/decorate" @@ -111,7 +111,7 @@ type AssetsDumper func(ctx context.Context, relSrc, dest string, uid int, gid in // 7a. If apparmor_parser fails, move /etc/apparmor.d/adsys/.old to /etc/apparmor.d/adsys/ // 7b. If apparmor_parser succeeds, remove /etc/apparmor.d/adsys/.old. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry, assetsDumper AssetsDumper) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply apparmor policy to %s"), objectName) + defer decorate.OnError(&err, gotext.Get("can't apply apparmor policy to %s", objectName)) objectDir := "machine" if !isComputer { @@ -131,7 +131,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer return err } // Otherwise, just let the user know - log.Warningf(ctx, i18n.G("Apparmor is not available on this system: %v"), err) + log.Warning(ctx, gotext.Get("Apparmor is not available on this system: %v", err)) return nil } m.apparmorParserCmd[0] = absPath @@ -139,13 +139,13 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // If we have no entries, attempt to unload them and remove the apparmor directory idx := slices.IndexFunc(entries, func(e entry.Entry) bool { return e.Key == fmt.Sprintf("apparmor-%s", objectDir) }) if idx == -1 || entries[idx].Disabled { - log.Debugf(ctx, fmt.Sprintf(i18n.G("No entries found for the apparmor %s policy"), objectDir)) + log.Debug(ctx, gotext.Get("No entries found for the apparmor %s policy", objectDir)) return m.unloadAllRules(ctx, objectName, isComputer) } - log.Debugf(ctx, i18n.G("Applying apparmor %s policy to %s"), objectDir, objectName) + log.Debug(ctx, gotext.Get("Applying apparmor %s policy to %s", objectDir, objectName)) if err := os.MkdirAll(apparmorPath, 0750); err != nil { - return fmt.Errorf(i18n.G("can't create apparmor directory %q: %v"), apparmorPath, err) + return errors.New(gotext.Get("can't create apparmor directory %q: %v", apparmorPath, err)) } switch objectDir { @@ -160,7 +160,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // applyUserPolicy applies apparmor policies for the machine object. func (m *Manager) applyMachinePolicy(ctx context.Context, e entry.Entry, apparmorPath string, assetsDumper AssetsDumper) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply machine policy")) + defer decorate.OnError(&err, gotext.Get("can't apply machine policy")) existingProfiles, err := filesInDir(apparmorPath) if err != nil { @@ -183,11 +183,11 @@ func (m *Manager) applyMachinePolicy(ctx context.Context, e entry.Entry, apparmo // Remove any existing stale directories oldApparmorPath := apparmorPath + ".old" if err := os.RemoveAll(oldApparmorPath); err != nil { - return fmt.Errorf(i18n.G("can't remove old apparmor directory %q: %v"), oldApparmorPath, err) + return errors.New(gotext.Get("can't remove old apparmor directory %q: %v", oldApparmorPath, err)) } newApparmorPath := apparmorPath + ".new" if err := os.RemoveAll(newApparmorPath); err != nil { - return fmt.Errorf(i18n.G("can't remove new apparmor directory %q: %v"), newApparmorPath, err) + return errors.New(gotext.Get("can't remove new apparmor directory %q: %v", newApparmorPath, err)) } // Dump assets to the adsys/machine.new/ subdirectory with correct @@ -199,13 +199,13 @@ func (m *Manager) applyMachinePolicy(ctx context.Context, e entry.Entry, apparmo // Rename existing apparmor policy to .old if err := os.Rename(apparmorPath, oldApparmorPath); err != nil { - return fmt.Errorf(i18n.G("can't rename apparmor directory %q to %q: %v"), apparmorPath, oldApparmorPath, err) + return errors.New(gotext.Get("can't rename apparmor directory %q to %q: %v", apparmorPath, oldApparmorPath, err)) } defer cleanupOldApparmorDir(ctx, oldApparmorPath, apparmorPath) // Rename new apparmor policy to current if err := os.Rename(newApparmorPath, apparmorPath); err != nil { - return fmt.Errorf(i18n.G("can't rename apparmor directory %q to %q: %v"), newApparmorPath, apparmorPath, err) + return errors.New(gotext.Get("can't rename apparmor directory %q to %q: %v", newApparmorPath, apparmorPath, err)) } // Get the list of files to run apparmor_parser on @@ -244,20 +244,20 @@ func (m *Manager) applyMachinePolicy(ctx context.Context, e entry.Entry, apparmo out, err := cmd.CombinedOutput() smbsafe.DoneExec() if err != nil { - return fmt.Errorf(i18n.G("failed to load apparmor rules: %w\n%s"), err, string(out)) + return errors.New(gotext.Get("failed to load apparmor rules: %v\n%s", err, string(out))) } } // Loading rules succeeded, remove old apparmor policy dir if err := os.RemoveAll(oldApparmorPath); err != nil { - return fmt.Errorf(i18n.G("can't remove old apparmor directory %q: %v"), oldApparmorPath, err) + return errors.New(gotext.Get("can't remove old apparmor directory %q: %v", oldApparmorPath, err)) } return nil } // applyUserPolicy applies apparmor policies for the user object. func (m *Manager) applyUserPolicy(ctx context.Context, e entry.Entry, apparmorPath string, username string, assetsDumper AssetsDumper) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply user policy")) + defer decorate.OnError(&err, gotext.Get("can't apply user policy")) // Create a temporary filepath to be used by the assets dumper and dump all // assets in order to get our user policy @@ -273,7 +273,7 @@ func (m *Manager) applyUserPolicy(ctx context.Context, e entry.Entry, apparmorPa // The user policy is always a single file if len(profilePaths) != 1 { - return fmt.Errorf(i18n.G("expected exactly one profile, got %d"), len(profilePaths)) + return errors.New(gotext.Get("expected exactly one profile, got %d", len(profilePaths))) } profilePath := profilePaths[0] profileContents, err := os.ReadFile(profilePath) @@ -301,7 +301,7 @@ func (m *Manager) applyUserPolicy(ctx context.Context, e entry.Entry, apparmorPa // Reload apparmor machine profiles to ensure that updates to the user policy are applied existingProfiles, err := filesInDir(filepath.Join(m.apparmorDir, "machine")) if errors.Is(err, os.ErrNotExist) { - log.Warningf(ctx, i18n.G("No apparmor machine profiles configured for this machine, skipping reload")) + log.Warningf(ctx, gotext.Get("No apparmor machine profiles configured for this machine, skipping reload")) return nil } if err != nil { @@ -325,11 +325,11 @@ func (m *Manager) applyUserPolicy(ctx context.Context, e entry.Entry, apparmorPa restoreErr = os.WriteFile(filepath.Join(apparmorPath, username), oldContent, 0600) } if restoreErr != nil { - log.Warningf(ctx, i18n.G("Failed to restore old apparmor user profile: %v"), restoreErr) + log.Warning(ctx, gotext.Get("Failed to restore old apparmor user profile: %v", restoreErr)) } // Return the execution error - return fmt.Errorf(i18n.G("failed to load apparmor rules: %w\n%s"), err, string(out)) + return errors.New(gotext.Get("failed to load apparmor rules: %v\n%s", err, string(out))) } return nil } @@ -340,7 +340,7 @@ func (m *Manager) applyUserPolicy(ctx context.Context, e entry.Entry, apparmorPa // If isComputer is true, only rules pertaining to the given user are unloaded. // No action is taken if the directory doesn't exist. func (m *Manager) unloadAllRules(ctx context.Context, objectName string, isComputer bool) (err error) { - defer decorate.OnError(&err, i18n.G("can't unload apparmor rules")) + defer decorate.OnError(&err, gotext.Get("can't unload apparmor rules")) machinePoliciesPath := filepath.Join(m.apparmorDir, "machine") pathToRemove := machinePoliciesPath @@ -417,12 +417,12 @@ func (m *Manager) policiesFromFiles(ctx context.Context, profiles []string) (pol err = cmd.Run() smbsafe.DoneExec() if err != nil { - return nil, fmt.Errorf(i18n.G("failed to get apparmor policies: %w\n%s"), err, errb.String()) + return nil, errors.New(gotext.Get("failed to get apparmor policies: %v\n%s", err, errb.String())) } // Execution succeeded but we still got something on stderr, let the user know if errb.Len() > 0 { - log.Warningf(ctx, i18n.G(`Got stderr output from apparmor_parser: -%s`), errb.String()) + log.Warning(ctx, gotext.Get(`Got stderr output from apparmor_parser: +%s`, errb.String())) } for _, line := range strings.Split(outb.String(), "\n") { @@ -443,7 +443,7 @@ func (m *Manager) unloadPolicies(ctx context.Context, policies []string) error { return nil } - log.Debugf(ctx, i18n.G("Unloading %d apparmor policies: %v"), len(policies), policies) + log.Debug(ctx, gotext.Get("Unloading %d apparmor policies: %v", len(policies), policies)) apparmorParserCmd := append(m.apparmorParserCmd, "-R") // #nosec G204 - We are in control of the arguments cmd := exec.CommandContext(ctx, apparmorParserCmd[0], apparmorParserCmd[1:]...) @@ -464,7 +464,7 @@ func (m *Manager) unloadPolicies(ctx context.Context, policies []string) error { } // For each policy, declare an empty block to remove it. if _, err := io.WriteString(stdin, fmt.Sprintf("profile %s {}\n", policy)); err != nil { - log.Warningf(ctx, i18n.G("Couldn't write to apparmor parser stdin: %v"), err) + log.Warning(ctx, gotext.Get("Couldn't write to apparmor parser stdin: %v", err)) } } }() @@ -472,7 +472,7 @@ func (m *Manager) unloadPolicies(ctx context.Context, policies []string) error { smbsafe.WaitExec() defer smbsafe.DoneExec() if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf(i18n.G("failed to unload apparmor policies: %w\n%s"), err, string(out)) + return errors.New(gotext.Get("failed to unload apparmor policies: %v\n%s", err, string(out))) } return nil } @@ -480,11 +480,11 @@ func (m *Manager) unloadPolicies(ctx context.Context, policies []string) error { // loadedPolicies parses the given system policies file and returns the list of // loaded apparmor policies. func (m *Manager) loadedPolicies() (policies []string, err error) { - defer decorate.OnError(&err, i18n.G("can't parse loaded apparmor policies")) + defer decorate.OnError(&err, gotext.Get("can't parse loaded apparmor policies")) file, err := os.Open(m.loadedPoliciesFile) if err != nil { - return nil, fmt.Errorf(i18n.G("failed to open %q: %w"), m.loadedPoliciesFile, err) + return nil, errors.New(gotext.Get("failed to open %q: %v", m.loadedPoliciesFile, err)) } defer file.Close() @@ -516,11 +516,11 @@ func cleanupOldApparmorDir(ctx context.Context, oldApparmorPath, apparmorPath st // Otherwise, we need to restore the old apparmor directory if err := os.RemoveAll(apparmorPath); err != nil { - log.Warningf(ctx, i18n.G("Couldn't remove new apparmor directory: %v"), err) + log.Warning(ctx, gotext.Get("Couldn't remove new apparmor directory: %v", err)) } if err := os.Rename(oldApparmorPath, apparmorPath); err != nil { - log.Warningf(ctx, i18n.G("Couldn't restore previous apparmor directory: %v"), err) + log.Warning(ctx, gotext.Get("Couldn't restore previous apparmor directory: %v", err)) } } @@ -537,10 +537,10 @@ func filesFromEntry(e entry.Entry, apparmorPath string) ([]string, error) { profileFilePath := filepath.Join(apparmorPath, profile) info, err := os.Stat(profileFilePath) if err != nil { - return nil, fmt.Errorf(i18n.G("apparmor profile %q is not accessible: %w"), err) + return nil, errors.New(gotext.Get("apparmor profile %q is not accessible: %v", profile, err)) } if info.IsDir() { - return nil, fmt.Errorf(i18n.G("apparmor profile %q is a directory and not a file"), profile) + return nil, errors.New(gotext.Get("apparmor profile %q is a directory and not a file", profile)) } // Clean and deduplicate the profile file paths @@ -556,7 +556,7 @@ func filesFromEntry(e entry.Entry, apparmorPath string) ([]string, error) { // removeUnusedAssets removes all files/directories in the given directory that // are not in the given list of files. func removeUnusedAssets(apparmorPath string, filesToKeep []string) (e error) { - defer decorate.OnError(&e, i18n.G("can't remove unused apparmor assets")) + defer decorate.OnError(&e, gotext.Get("can't remove unused apparmor assets")) return filepath.WalkDir(apparmorPath, func(path string, d os.DirEntry, err error) error { if err != nil { @@ -640,7 +640,7 @@ func intersection(a, b []string) []string { // writeIfChanged will only write to path if content is different from current content. func writeIfChanged(path string, content string) (oldContent []byte, changed bool, err error) { - defer decorate.OnError(&err, i18n.G("can't save %s"), path) + defer decorate.OnError(&err, gotext.Get("can't save %s", path)) oldContent, err = os.ReadFile(path) if err == nil && string(oldContent) == content { diff --git a/internal/policies/certificate/certificate.go b/internal/policies/certificate/certificate.go index a5bf87257..ced672509 100644 --- a/internal/policies/certificate/certificate.go +++ b/internal/policies/certificate/certificate.go @@ -19,6 +19,7 @@ import ( "context" _ "embed" // embed cert enroll python script "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -29,9 +30,9 @@ import ( "sync" "time" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/adsys/internal/smbsafe" "github.com/ubuntu/decorate" @@ -150,7 +151,7 @@ func New(domain string, opts ...Option) *Manager { // ApplyPolicy runs the certificate autoenrollment script to enroll or un-enroll the machine. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer, isOnline bool, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply certificate policy")) + defer decorate.OnError(&err, gotext.Get("can't apply certificate policy")) m.mu.Lock() defer m.mu.Unlock() @@ -161,7 +162,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer } if !isOnline { - log.Debug(ctx, i18n.G("AD backend is offline, skipping certificate policy")) + log.Debug(ctx, gotext.Get("AD backend is offline, skipping certificate policy")) return nil } @@ -185,7 +186,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer entry := entries[idx] value, err := strconv.Atoi(entry.Value) if err != nil { - return fmt.Errorf(i18n.G("failed to parse certificate policy entry value: %w"), err) + return errors.New(gotext.Get("failed to parse certificate policy entry value: %v", err)) } if value&disabledFlag == disabledFlag { @@ -206,7 +207,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer valuename := keyparts[len(keyparts)-1] gpoData, err := gpoData(entry.Value, valuename) if err != nil { - return fmt.Errorf(i18n.G("failed to parse policy entry value: %w"), err) + return errors.New(gotext.Get("failed to parse policy entry value: %v", err)) } polSrvRegistryEntries = append(polSrvRegistryEntries, gpoEntry{keyname, valuename, gpoData, gpoType(valuename)}) @@ -222,7 +223,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer jsonGPOData, err := json.Marshal(polSrvRegistryEntries) if err != nil { - return fmt.Errorf(i18n.G("failed to marshal policy server registry entries: %v"), err) + return errors.New(gotext.Get("failed to marshal policy server registry entries: %v", err)) } if err := m.runScript(ctx, action, objectName, "--policy_servers_json", string(jsonGPOData)); err != nil { @@ -251,9 +252,9 @@ func (m *Manager) runScript(ctx context.Context, action, objectName string, extr output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf(i18n.G("failed to run certificate autoenrollment script (exited with %d): %v\n%s"), cmd.ProcessState.ExitCode(), err, string(output)) + return errors.New(gotext.Get("failed to run certificate autoenrollment script (exited with %d): %v\n%s", cmd.ProcessState.ExitCode(), err, string(output))) } - log.Infof(ctx, i18n.G("Certificate autoenrollment script ran successfully\n%s"), string(output)) + log.Info(ctx, gotext.Get("Certificate autoenrollment script ran successfully\n%s", string(output))) return nil } diff --git a/internal/policies/dconf/dconf.go b/internal/policies/dconf/dconf.go index c8f2a485c..c5ee738db 100644 --- a/internal/policies/dconf/dconf.go +++ b/internal/policies/dconf/dconf.go @@ -40,9 +40,9 @@ import ( "sync" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/adsys/internal/smbsafe" "github.com/ubuntu/decorate" @@ -66,7 +66,7 @@ func NewWithDconfDir(dir string) *Manager { // ApplyPolicy generates a dconf computer or user policy based on a list of entries. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply dconf policy to %s"), objectName) + defer decorate.OnError(&err, gotext.Get("can't apply dconf policy to %s", objectName)) dconfDir := m.dconfDir if dconfDir == "" { @@ -97,7 +97,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer if !isComputer && len(entries) > 0 { if _, err := os.Stat(filepath.Join(dbsPath, "machine.d", "locks", "adsys")); err != nil { - return fmt.Errorf(i18n.G("machine dconf database is required before generating a policy for an user. This one returns: %v"), err) + return errors.New(gotext.Get("machine dconf database is required before generating a policy for an user. This one returns: %v", err)) } } @@ -105,13 +105,13 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // We don't clean up the machine database because we don't know if there's any user GPO depending on it. if !isComputer && len(entries) == 0 { if err := os.RemoveAll(dbPath); err != nil { - return fmt.Errorf(i18n.G("can't remove user dconf database directory: %v"), err) + return errors.New(gotext.Get("can't remove user dconf database directory: %v", err)) } if er := os.RemoveAll(filepath.Join(dbsPath, objectName)); er != nil { - return fmt.Errorf(i18n.G("can't remove user dconf binary database: %v"), er) + return errors.New(gotext.Get("can't remove user dconf binary database: %v", err)) } if err := os.RemoveAll(filepath.Join(profilesPath, objectName)); err != nil { - return fmt.Errorf(i18n.G("can't remove user dconf profile: %v"), err) + return errors.New(gotext.Get("can't remove user dconf profile: %v", err)) } return nil } @@ -141,7 +141,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // normalize common user error cases and check gsettings schema signature match. e.Value = normalizeValue(e.Meta, e.Value) if err := checkSignature(e.Meta, e.Value); err != nil { - errMsgs = append(errMsgs, fmt.Sprintf(i18n.G("- error on %s: %v"), e.Key, err)) + errMsgs = append(errMsgs, gotext.Get("- error on %s: %v", e.Key, err)) continue } @@ -210,7 +210,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer m.dconfUpdateMu.Unlock() smbsafe.DoneExec() if errExec != nil { - err = fmt.Errorf(i18n.G("dconf update failed: %v"), out) + err = errors.New(gotext.Get("dconf update failed: %v", out)) } return nil @@ -218,7 +218,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // writeIfChanged will only write to path if content is different from current content. func writeIfChanged(path string, content string) (done bool, err error) { - defer decorate.OnError(&err, i18n.G("can't save %s"), path) + defer decorate.OnError(&err, gotext.Get("can't save %s", path)) if oldContent, err := os.ReadFile(path); err == nil && string(oldContent) == content { return false, nil @@ -239,7 +239,7 @@ func writeIfChanged(path string, content string) (done bool, err error) { // The adsys system-db should always be the first system-db in the file to enforce their values // (upper system-db in the profile wins). func writeProfile(ctx context.Context, user, profilesPath string) (err error) { - defer decorate.OnError(&err, i18n.G("can't update user profile %s"), profilesPath) + defer decorate.OnError(&err, gotext.Get("can't update user profile %s", profilesPath)) profilePath := filepath.Join(profilesPath, user) log.Debugf(ctx, "Update user profile %s", profilePath) @@ -430,19 +430,19 @@ func splitOnNonEscaped(v, sep string) []string { // checkSignature returns an error if the value doesn't match the expected variant signature. func checkSignature(meta, value string) (err error) { - defer decorate.OnError(&err, i18n.G("error while checking signature")) + defer decorate.OnError(&err, gotext.Get("error while checking signature")) if meta == "" { - return fmt.Errorf(i18n.G("empty signature for %v"), meta) + return errors.New(gotext.Get("empty signature for %v", meta)) } sig, err := dbus.ParseSignature(meta) if err != nil { - return fmt.Errorf(i18n.G("%s is not a valid gsettings signature: %v"), meta, err) + return errors.New(gotext.Get("%s is not a valid gsettings signature: %v", meta, err)) } _, err = dbus.ParseVariant(value, sig) if err != nil { - return fmt.Errorf(i18n.G("can't parse %q as %q: %v"), value, meta, err) + return errors.New(gotext.Get("can't parse %q as %q: %v", value, meta, err)) } return nil diff --git a/internal/policies/gdm/gdm.go b/internal/policies/gdm/gdm.go index 1792dec7b..a36125420 100644 --- a/internal/policies/gdm/gdm.go +++ b/internal/policies/gdm/gdm.go @@ -9,8 +9,8 @@ import ( "context" "strings" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/dconf" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" @@ -37,7 +37,7 @@ func WithDconf(m *dconf.Manager) func(o *options) error { // New returns a new manager for gdm policy handlers. func New(opts ...option) (m *Manager, err error) { - defer decorate.OnError(&err, i18n.G("can't create a new gdm handler manager")) + defer decorate.OnError(&err, gotext.Get("can't create a new gdm handler manager")) // defaults args := options{ @@ -57,7 +57,7 @@ func New(opts ...option) (m *Manager, err error) { // ApplyPolicy generates a dconf computer or user policy based on a list of entries. func (m *Manager) ApplyPolicy(ctx context.Context, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply gdm policy")) + defer decorate.OnError(&err, gotext.Get("can't apply gdm policy")) log.Debug(ctx, "ApplyPolicy gdm policy") diff --git a/internal/policies/manager.go b/internal/policies/manager.go index c587c34b5..c331e73f5 100644 --- a/internal/policies/manager.go +++ b/internal/policies/manager.go @@ -25,6 +25,7 @@ package policies import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -34,10 +35,10 @@ import ( "time" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/ad/backends" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/apparmor" "github.com/ubuntu/adsys/internal/policies/certificate" "github.com/ubuntu/adsys/internal/policies/dconf" @@ -238,7 +239,7 @@ func WithCertAutoenrollCmd(cmd []string) Option { // NewManager returns a new manager with all default policy handlers. func NewManager(bus *dbus.Conn, hostname string, backend backends.Backend, opts ...Option) (m *Manager, err error) { - defer decorate.OnError(&err, i18n.G("can't create a new policy handlers manager")) + defer decorate.OnError(&err, gotext.Get("can't create a new policy handlers manager")) defaultSystemdCaller, err := systemd.New(bus) if err != nil { @@ -351,7 +352,7 @@ func NewManager(bus *dbus.Conn, hostname string, backend backends.Backend, opts // ApplyPolicies generates a computer or user policy based on a list of entries // retrieved from a directory service. func (m *Manager) ApplyPolicies(ctx context.Context, objectName string, isComputer bool, pols *Policies) (err error) { - defer decorate.OnError(&err, i18n.G("failed to apply policy to %q"), objectName) + defer decorate.OnError(&err, gotext.Get("failed to apply policy to %q", objectName)) // We have a lock per objectName to prevent multiple instances of ApplyPolicies for the same object. m.muMu.Lock() @@ -363,11 +364,11 @@ func (m *Manager) ApplyPolicies(ctx context.Context, objectName string, isComput m.muMu.Unlock() rules := pols.GetUniqueRules() - action := i18n.G("Applying") + action := gotext.Get("Applying") if len(rules) == 0 { - action = i18n.G("Unloading") + action = gotext.Get("Unloading") } - log.Infof(ctx, i18n.G("%s policies for %s (machine: %v)"), action, objectName, isComputer) + log.Info(ctx, gotext.Get("%s policies for %s (machine: %v)", action, objectName, isComputer)) var g errgroup.Group // Applying dconf policies take a while to complete, so it's better to start applying them before @@ -377,7 +378,7 @@ func (m *Manager) ApplyPolicies(ctx context.Context, objectName string, isComput }) if !m.GetSubscriptionState(ctx) { if filteredRules := filterRules(ctx, rules); len(filteredRules) > 0 { - log.Warningf(ctx, i18n.G("Rules from the following policy types will be filtered out as the machine is not enrolled to Ubuntu Pro: %s"), strings.Join(filteredRules, ", ")) + log.Warning(ctx, gotext.Get("Rules from the following policy types will be filtered out as the machine is not enrolled to Ubuntu Pro: %s", strings.Join(filteredRules, ", "))) } } @@ -419,7 +420,7 @@ func (m *Manager) ApplyPolicies(ctx context.Context, objectName string, isComput // DumpPolicies displays the currently applied policies and rules (since last update) for objectName. // It can in addition show the rules and overridden content. func (m *Manager) DumpPolicies(ctx context.Context, objectName string, computerOnly, withRules, withOverridden bool) (msg string, err error) { - defer decorate.OnError(&err, i18n.G("failed to dump policies for %q"), objectName) + defer decorate.OnError(&err, gotext.Get("failed to dump policies for %q", objectName)) log.Infof(ctx, "Dumping policies for %s", objectName) @@ -427,22 +428,22 @@ func (m *Manager) DumpPolicies(ctx context.Context, objectName string, computerO var alreadyProcessedRules map[string]struct{} if !computerOnly { - fmt.Fprintln(&out, i18n.G("Policies from machine configuration:")) + fmt.Fprintln(&out, gotext.Get("Policies from machine configuration:")) policiesHost, err := NewFromCache(ctx, filepath.Join(m.policiesCacheDir, m.hostname)) if err != nil { - return "", fmt.Errorf(i18n.G("no policy applied for %q: %v"), m.hostname, err) + return "", errors.New(gotext.Get("no policy applied for %q: %v", m.hostname, err)) } for _, g := range policiesHost.GPOs { alreadyProcessedRules = g.Format(&out, withRules, withOverridden, alreadyProcessedRules) } - fmt.Fprintln(&out, i18n.G("Policies from user configuration:")) + fmt.Fprintln(&out, gotext.Get("Policies from user configuration:")) } // Load target policies policiesTarget, err := NewFromCache(ctx, filepath.Join(m.policiesCacheDir, objectName)) if err != nil { - log.Infof(ctx, i18n.G("User %q not found on cache."), objectName) - return "", fmt.Errorf(i18n.G("no policy applied for %q: %v"), objectName, err) + log.Info(ctx, gotext.Get("User %q not found on cache.", objectName)) + return "", errors.New(gotext.Get("no policy applied for %q: %v", objectName, err)) } for _, g := range policiesTarget.GPOs { alreadyProcessedRules = g.Format(&out, withRules, withOverridden, alreadyProcessedRules) @@ -453,7 +454,7 @@ func (m *Manager) DumpPolicies(ctx context.Context, objectName string, computerO // LastUpdateFor returns the last update time for object or current machine. func (m *Manager) LastUpdateFor(ctx context.Context, objectName string, isMachine bool) (t time.Time, err error) { - defer decorate.OnError(&err, i18n.G("failed to get policy last update time %q (machine: %q)"), objectName, isMachine) + defer decorate.OnError(&err, gotext.Get("failed to get policy last update time %q (machine: %v)", objectName, isMachine)) log.Infof(ctx, "Get policies last update time %q (machine: %t)", objectName, isMachine) @@ -463,7 +464,7 @@ func (m *Manager) LastUpdateFor(ctx context.Context, objectName string, isMachin info, err := os.Stat(filepath.Join(m.policiesCacheDir, objectName)) if err != nil { - return time.Time{}, fmt.Errorf(i18n.G("policies were not applied for %q: %v"), objectName, err) + return time.Time{}, errors.New(gotext.Get("policies were not applied for %q: %v", objectName, err)) } return info.ModTime(), nil } diff --git a/internal/policies/mount/mount.go b/internal/policies/mount/mount.go index 29711a95a..91c496941 100644 --- a/internal/policies/mount/mount.go +++ b/internal/policies/mount/mount.go @@ -26,8 +26,8 @@ import ( "strings" "github.com/coreos/go-systemd/v22/unit" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" ) @@ -65,7 +65,7 @@ type systemdCaller interface { // New creates a Manager to handle mount policies. func New(runDir string, systemUnitDir string, systemdCaller systemdCaller, opts ...Option) (m *Manager, err error) { - defer decorate.OnError(&err, i18n.G("failed to create new mount manager")) + defer decorate.OnError(&err, gotext.Get("failed to create new mount manager")) o := options{ userLookup: user.Lookup, @@ -102,7 +102,7 @@ func New(runDir string, systemUnitDir string, systemdCaller systemdCaller, opts // ApplyPolicy generates mount policies based on a list of entries. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply mount policy to %s"), objectName) + defer decorate.OnError(&err, gotext.Get("can't apply mount policy to %s", objectName)) log.Debugf(ctx, "Applying mount policy to %s", objectName) @@ -120,12 +120,12 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer }) if i == -1 { - log.Debugf(ctx, i18n.G("The provided entries are not supported by the %s mount manager: %v"), key, entries) + log.Debug(ctx, gotext.Get("The provided entries are not supported by the %s mount manager: %v", key, entries)) return m.cleanup(ctx, objectName, isComputer) } if entries[i].Disabled { - log.Debugf(ctx, i18n.G("The entry %q is disabled and will be skipped"), entries[i].Key) + log.Debug(ctx, gotext.Get("The entry %q is disabled and will be skipped", entries[i].Key)) return m.cleanup(ctx, objectName, isComputer) } @@ -136,20 +136,20 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer } func (m *Manager) applyUserMountsPolicy(ctx context.Context, username string, entry entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("failed to apply policy for user %q"), username) + defer decorate.OnError(&err, gotext.Get("failed to apply policy for user %q", username)) log.Debugf(ctx, "Applying mount policy to user %q", username) var uid, gid int u, err := m.userLookup(username) if err != nil { - return fmt.Errorf(i18n.G("could not retrieve user for %q: %w"), err) + return errors.New(gotext.Get("could not retrieve user for %q: %v", username, err)) } if uid, err = strconv.Atoi(u.Uid); err != nil { - return fmt.Errorf(i18n.G("couldn't convert %q to a valid uid for %q"), u.Uid, username) + return errors.New(gotext.Get("couldn't convert %q to a valid uid for %q", u.Uid, username)) } if gid, err = strconv.Atoi(u.Gid); err != nil { - return fmt.Errorf(i18n.G("couldn't convert %q to a valid gid for %q"), u.Gid, username) + return errors.New(gotext.Get("couldn't convert %q to a valid gid for %q", u.Gid, username)) } objectPath := filepath.Join(m.runDir, "users", u.Uid) @@ -157,7 +157,7 @@ func (m *Manager) applyUserMountsPolicy(ctx context.Context, username string, en // This creates the user directory and set its ownership to the current user. if err := mkdirAllWithUIDGID(objectPath, uid, gid); err != nil { - return fmt.Errorf(i18n.G("can't create user directory %q for %q: %w"), objectPath, username, err) + return errors.New(gotext.Get("can't create user directory %q for %q: %v", objectPath, username, err)) } parsedValues, err := parseEntryValues(ctx, entry) @@ -181,9 +181,9 @@ func (m *Manager) applyUserMountsPolicy(ctx context.Context, username string, en } func (m *Manager) applySystemMountsPolicy(ctx context.Context, machineName string, entry entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("error when applying mount policy to machine %q"), machineName) + defer decorate.OnError(&err, gotext.Get("error when applying mount policy to machine %q", machineName)) - log.Debugf(ctx, i18n.G("Applying mount policy to machine %q"), machineName) + log.Debug(ctx, gotext.Get("Applying mount policy to machine %q", machineName)) parsedValues, err := parseEntryValues(ctx, entry) if err != nil { @@ -237,7 +237,7 @@ func (m *Manager) applySystemMountsPolicy(ctx context.Context, machineName strin return err } if err := m.systemdCaller.StartUnit(ctx, name); err != nil { - log.Warningf(ctx, i18n.G("failed to start unit %q: %v"), name, err) + log.Warning(ctx, gotext.Get("failed to start unit %q: %v", name, err)) } } @@ -347,10 +347,10 @@ func whatStringFromInfo(mi mountInfo) string { // parseEntryValues parses the entry value, trimming whitespaces and removing duplicates. func parseEntryValues(ctx context.Context, e entry.Entry) (p []string, err error) { - defer decorate.OnError(&err, i18n.G("failed to parse entry values")) + defer decorate.OnError(&err, gotext.Get("failed to parse entry values")) if e.Err != nil { - return nil, fmt.Errorf(i18n.G("entry is errored: %w"), e.Err) + return nil, errors.New(gotext.Get("entry is errored: %v", e.Err)) } seen := make(map[string]string) @@ -364,9 +364,9 @@ func parseEntryValues(ctx context.Context, e entry.Entry) (p []string, err error tmp := strings.TrimPrefix(v, krbTag) if prev, ok := seen[tmp]; ok { if prev == v { - log.Debugf(ctx, i18n.G("Value %q is duplicated."), v) + log.Debug(ctx, gotext.Get("Value %q is duplicated.", v)) } else { - log.Warningf(ctx, i18n.G("The location %q was already set up to be mounted with different options or authentication. The first provided value %q will be used instead."), v, prev) + log.Warning(ctx, gotext.Get("The location %q was already set up to be mounted with different options or authentication. The first provided value %q will be used instead.", v, prev)) } continue } @@ -389,7 +389,7 @@ func checkValue(value string) error { // Value left: protocol:/// if _, hostnameAndPath, found := strings.Cut(tmp, ":"); !found || !strings.HasPrefix(hostnameAndPath, "//") { - return fmt.Errorf(i18n.G("entry %q is badly formatted"), value) + return errors.New(gotext.Get("entry %q is badly formatted", value)) } return nil @@ -397,7 +397,7 @@ func checkValue(value string) error { // writeIfChanged will only write to path if content is different from current content. func writeIfChanged(path string, content string) (done bool, err error) { - defer decorate.OnError(&err, i18n.G("can't save %s"), path) + defer decorate.OnError(&err, gotext.Get("can't save %s", path)) if oldContent, err := os.ReadFile(path); err == nil && string(oldContent) == content { return false, nil @@ -416,7 +416,7 @@ func writeIfChanged(path string, content string) (done bool, err error) { // writeFileWithUIDGID writes the content into the specified path and changes its ownership to the specified uid/gid. func writeFileWithUIDGID(path string, uid, gid int, content string) (err error) { - defer decorate.OnError(&err, i18n.G("failed when writing file %s"), path) + defer decorate.OnError(&err, gotext.Get("failed when writing file %s", path)) // #nosec G306. This should be world-readable. if err = os.WriteFile(path+".new", []byte(content+"\n"), 0600); err != nil { @@ -438,7 +438,7 @@ func writeFileWithUIDGID(path string, uid, gid int, content string) (err error) // mkdirAllWithUIDGID create a directory and sets its ownership to the specified uid and gid. func mkdirAllWithUIDGID(p string, uid, gid int) error { if err := os.MkdirAll(p, 0750); err != nil { - return fmt.Errorf(i18n.G("can't create directory %q: %v"), p, err) + return fmt.Errorf(gotext.Get("can't create directory %q: %v", p, err)) } return chown(p, nil, uid, gid) @@ -447,7 +447,7 @@ func mkdirAllWithUIDGID(p string, uid, gid int) error { // chown either chown the file descriptor attached, or the path if this one is null to uid and gid. // It will know if we should skip chown for tests. func chown(p string, f *os.File, uid, gid int) (err error) { - defer decorate.OnError(&err, i18n.G("can't chown %q"), p) + defer decorate.OnError(&err, gotext.Get("can't chown %q", p)) if os.Getenv("ADSYS_SKIP_ROOT_CALLS") != "" { uid = -1 @@ -464,9 +464,9 @@ func chown(p string, f *os.File, uid, gid int) (err error) { // cleanup removes the files generated when applying the mount policy to an object. func (m *Manager) cleanup(ctx context.Context, objectName string, isComputer bool) (err error) { - defer decorate.OnError(&err, i18n.G("failed to clean up mount policy files for %q"), objectName) + defer decorate.OnError(&err, gotext.Get("failed to clean up mount policy files for %q", objectName)) - log.Debugf(ctx, i18n.G("Cleaning up mount policy files for %q"), objectName) + log.Debugf(ctx, gotext.Get("Cleaning up mount policy files for %q", objectName)) if !isComputer { var u *user.User @@ -485,9 +485,9 @@ func (m *Manager) cleanup(ctx context.Context, objectName string, isComputer boo // cleanupMountsFile removes the mounts file, if there is any, created for the user with the specified uid. func (m *Manager) cleanupMountsFile(ctx context.Context, uid string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to clean up mounts file")) + defer decorate.OnError(&err, gotext.Get("failed to clean up mounts file")) - log.Debugf(ctx, i18n.G("Cleaning up mounts file for user with uid %q"), uid) + log.Debug(ctx, gotext.Get("Cleaning up mounts file for user with uid %q", uid)) p := filepath.Join(m.runDir, "users", uid, "mounts") @@ -501,12 +501,12 @@ func (m *Manager) cleanupMountsFile(ctx context.Context, uid string) (err error) // cleanupMountUnits removes all the mount units generated by adsys for the current system. func (m *Manager) cleanupMountUnits(ctx context.Context, units []string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to clean up the mount units")) + defer decorate.OnError(&err, gotext.Get("failed to clean up the mount units")) for _, unit := range units { // Tries to stop the unit before disabling and removing it. if err := m.systemdCaller.StopUnit(ctx, unit); err != nil { - log.Warningf(ctx, i18n.G("Failed to stop unit %q: %v"), unit, err) + log.Warning(ctx, gotext.Get("Failed to stop unit %q: %v", unit, err)) } // Disables the unit before removing it. @@ -515,7 +515,7 @@ func (m *Manager) cleanupMountUnits(ctx context.Context, units []string) (err er } if err := os.Remove(filepath.Join(m.systemUnitDir, unit)); err != nil { - return fmt.Errorf(i18n.G("could not remove file %q: %w"), unit, err) + return errors.New(gotext.Get("could not remove file %q: %v", unit, err)) } } diff --git a/internal/policies/policies.go b/internal/policies/policies.go index 5bb5ad3ae..0e42d0fe2 100644 --- a/internal/policies/policies.go +++ b/internal/policies/policies.go @@ -8,7 +8,6 @@ import ( "archive/zip" "context" "errors" - "fmt" "io" "io/fs" "os" @@ -16,8 +15,8 @@ import ( "sort" "strings" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" "golang.org/x/exp/mmap" @@ -47,7 +46,7 @@ type Policies struct { // We pass directly the compressed DB and don’t save immediately in cache as we will do it // once the gpos are all treated. func New(ctx context.Context, gpos []GPO, assetsDBPath string) (pols Policies, err error) { - defer decorate.OnError(&err, i18n.G("can't created new policies")) + defer decorate.OnError(&err, gotext.Get("can't created new policies")) log.Debugf(ctx, "Creating new policies") @@ -67,7 +66,7 @@ func New(ctx context.Context, gpos []GPO, assetsDBPath string) (pols Policies, e // NewFromCache returns cached policies loaded from the p cache directory. func NewFromCache(ctx context.Context, p string) (pols Policies, err error) { - defer decorate.OnError(&err, i18n.G("can't get cached policies from %s"), p) + defer decorate.OnError(&err, gotext.Get("can't get cached policies from %s", p)) log.Debugf(ctx, "Loading policies from cache using %s", p) @@ -107,7 +106,7 @@ func openAssetsInMemory(assetsDB string) (assets *assetsFromMMAP, err error) { r, err := zip.NewReader(f, int64(f.Len())) if err != nil { - return nil, fmt.Errorf(i18n.G("invalid zip db archive: %w"), err) + return nil, errors.New(gotext.Get("invalid zip db archive: %v", err)) } return &assetsFromMMAP{ @@ -120,7 +119,7 @@ func openAssetsInMemory(assetsDB string) (assets *assetsFromMMAP, err error) { // Save serializes in p policies. // Do not save again if p is already the origin. We don’t allow modifying GPOs or assets on the object. func (pols *Policies) Save(p string) (err error) { - defer decorate.OnError(&err, i18n.G("can't save policies to %s"), p) + defer decorate.OnError(&err, gotext.Get("can't save policies to %s", p)) if err := os.MkdirAll(p, 0700); err != nil { return err @@ -218,17 +217,17 @@ func (r *readerAtToReader) Read(p []byte) (n int, err error) { // The destination directory or file should not exists. // A uid or gid different from -1 means that every directories and files will be chown to that user and group. func (pols *Policies) SaveAssetsTo(ctx context.Context, relSrc, dest string, uid, gid int) (err error) { - defer decorate.OnError(&err, i18n.G("can't save assets to %s"), dest) + defer decorate.OnError(&err, gotext.Get("can't save assets to %s", dest)) log.Debugf(ctx, "export assets %q to %q", relSrc, dest) if pols.assets == nil { - return errors.New(i18n.G("no assets attached")) + return errors.New(gotext.Get("no assets attached")) } // error out if dest exists if _, err := os.Stat(dest); !errors.Is(err, fs.ErrNotExist) { - return fmt.Errorf(i18n.G("destination %q already exists"), dest) + return errors.New(gotext.Get("destination %q already exists", dest)) } baseDir := strings.TrimSuffix(relSrc, "/") @@ -240,7 +239,7 @@ func (pols *Policies) saveAssetsRecursively(relSrc, dest, baseDir string, uid, g relSrc = strings.TrimSuffix(relSrc, "/") if relSrc == "" { - return errors.New(i18n.G("no relSrc provided to look into database archive")) + return errors.New(gotext.Get("no relSrc provided to look into database archive")) } dstPath := filepath.Join(dest, strings.TrimPrefix(relSrc, baseDir)) @@ -300,7 +299,7 @@ func (pols *Policies) saveAssetsRecursively(relSrc, dest, baseDir string, uid, g // CompressAssets allow compressing all assets from SYSVOL in a single zip file. func CompressAssets(ctx context.Context, p string) (err error) { - defer decorate.OnError(&err, i18n.G("can't compress assets from %s"), p) + defer decorate.OnError(&err, gotext.Get("can't compress assets from %s", p)) log.Debugf(ctx, "compress assets from %q", p) @@ -431,7 +430,7 @@ func (pols Policies) GetUniqueRules() map[string][]entry.Entry { // chown either chown the file descriptor attached, or the path if this one is null to uid and gid. // It will know if we should skip chown for tests. func chown(p string, f *os.File, uid, gid int) (err error) { - defer decorate.OnError(&err, i18n.G("can't chown %q"), p) + defer decorate.OnError(&err, gotext.Get("can't chown %q", p)) if os.Getenv("ADSYS_SKIP_ROOT_CALLS") != "" { uid = -1 diff --git a/internal/policies/privilege/privilege.go b/internal/policies/privilege/privilege.go index 6c671a5a9..17631e568 100644 --- a/internal/policies/privilege/privilege.go +++ b/internal/policies/privilege/privilege.go @@ -25,9 +25,9 @@ import ( "sort" "strings" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" "gopkg.in/ini.v1" @@ -66,7 +66,7 @@ func NewWithDirs(sudoersDir, policyKitDir string) *Manager { // ApplyPolicy generates a privilege policy based on a list of entries. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply privilege policy to %s"), objectName) + defer decorate.OnError(&err, gotext.Get("can't apply privilege policy to %s", objectName)) // We only have privilege escalation on computers. if !isComputer { @@ -247,7 +247,7 @@ func splitAndNormalizeUsersAndGroups(ctx context.Context, v string) []string { // It lists /etc/polkit-1/localauthority.conf.d and take the highest file in ascii order to match // from the [configuration] section AdminIdentities value. func getSystemPolkitAdminIdentities(ctx context.Context, policyKitDir string) (adminIdentities string, err error) { - defer decorate.OnError(&err, i18n.G("can't get existing system polkit administrators in %s"), policyKitDir) + defer decorate.OnError(&err, gotext.Get("can't get existing system polkit administrators in %s", policyKitDir)) polkitConfFiles, err := filepath.Glob(filepath.Join(policyKitDir, "localauthority.conf.d", "*.conf")) if err != nil { @@ -260,7 +260,7 @@ func getSystemPolkitAdminIdentities(ctx context.Context, policyKitDir string) (a return "", err } if fi.IsDir() { - log.Warningf(ctx, i18n.G("%s is a directory. Ignoring."), p) + log.Warning(ctx, gotext.Get("%s is a directory. Ignoring.", p)) continue } diff --git a/internal/policies/proxy/proxy.go b/internal/policies/proxy/proxy.go index b0db2ef5a..7324af98c 100644 --- a/internal/policies/proxy/proxy.go +++ b/internal/policies/proxy/proxy.go @@ -19,8 +19,8 @@ import ( "strings" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" ) @@ -76,7 +76,7 @@ func New(bus *dbus.Conn, args ...Option) *Manager { // ApplyPolicy applies the system proxy policy (via a D-Bus call to ubuntu-proxy-manager). func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply proxy policy")) + defer decorate.OnError(&err, gotext.Get("can't apply proxy policy")) // Proxy policies are currently only supported on computers if !isComputer { @@ -92,7 +92,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer for _, e := range entries { key := e.Key[strings.LastIndex(e.Key, "/")+1:] if !slices.Contains(supportedKeys, key) { - log.Warningf(ctx, i18n.G("Encountered unsupported key '%s' while parsing proxy entries, skipping it"), key) + log.Warning(ctx, gotext.Get("Encountered unsupported key '%s' while parsing proxy entries, skipping it", key)) } args[key] = e.Value } @@ -111,7 +111,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer args["auto"]).Err; err != nil { var dbusErr dbus.Error if errors.As(err, &dbusErr) && dbusErr.Name == errDBusServiceUnknownName { - log.Warningf(ctx, i18n.G("Not applying proxy settings as ubuntu-proxy-manager is not installed: %s"), dbusErr.Error()) + log.Warning(ctx, gotext.Get("Not applying proxy settings as ubuntu-proxy-manager is not installed: %s", dbusErr.Error())) return nil } return err diff --git a/internal/policies/scripts/scripts.go b/internal/policies/scripts/scripts.go index 675eab84d..b53ab4265 100644 --- a/internal/policies/scripts/scripts.go +++ b/internal/policies/scripts/scripts.go @@ -25,9 +25,9 @@ import ( "strconv" "strings" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/policies/entry" "github.com/ubuntu/decorate" ) @@ -59,7 +59,7 @@ type Option func(*options) // New creates a manager with a specific scripts directory. func New(runDir string, unitStarter unitStarter, opts ...Option) (m *Manager, err error) { - defer decorate.OnError(&err, i18n.G("can't create scripts manager")) + defer decorate.OnError(&err, gotext.Get("can't create scripts manager")) // defaults args := options{ @@ -93,7 +93,7 @@ type AssetsDumper func(ctx context.Context, relSrc, dest string, uid int, gid in // ApplyPolicy generates a privilege policy based on a list of entries. func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer bool, entries []entry.Entry, assetsDumper AssetsDumper) (err error) { - defer decorate.OnError(&err, i18n.G("can't apply scripts policy to %s"), objectName) + defer decorate.OnError(&err, gotext.Get("can't apply scripts policy to %s", objectName)) log.Debugf(ctx, "Applying scripts policy to %s", objectName) @@ -102,13 +102,13 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer if !isComputer { user, err := m.userLookup(objectName) if err != nil { - return fmt.Errorf(i18n.G("couldn't retrieve user for %q: %v"), objectName, err) + return errors.New(gotext.Get("couldn't retrieve user for %q: %v", objectName, err)) } if uid, err = strconv.Atoi(user.Uid); err != nil { - return fmt.Errorf(i18n.G("couldn't convert %q to a valid uid for %q"), user.Uid, objectName) + return errors.New(gotext.Get("couldn't convert %q to a valid uid for %q", user.Uid, objectName)) } if gid, err = strconv.Atoi(user.Gid); err != nil { - return fmt.Errorf(i18n.G("couldn't convert %q to a valid gid for %q"), user.Gid, objectName) + return errors.New(gotext.Get("couldn't convert %q to a valid gid for %q", user.Gid, objectName)) } objectDir = filepath.Join("users", user.Uid) @@ -137,10 +137,10 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // This creates objectDirPath and scriptsDir directory. // We chown objectDirPath and scripts (user specific) to uid:gid of the user. Nothing is done for the machine if err := mkdirAllWithUIDGid(objectPath, uid, gid); err != nil { - return fmt.Errorf(i18n.G("can't create object directory %q: %v"), objectPath, err) + return errors.New(gotext.Get("can't create object directory %q: %v", objectPath, err)) } if err := mkdirAllWithUIDGid(scriptsPath, uid, gid); err != nil { - return fmt.Errorf(i18n.G("can't create scripts directory %q: %v"), scriptsPath, err) + return errors.New(gotext.Get("can't create scripts directory %q: %v", scriptsPath, err)) } // Dump assets to scripts/scripts/ subdirectory with correct ownership. If no assets is present while entries != nil, we want to return an error. @@ -165,14 +165,14 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer log.Debugf(ctx, "%q: found %q. Marking as executable %q", e.Key, script, scriptFilePath) info, err := os.Stat(scriptFilePath) if errors.Is(err, os.ErrNotExist) { - return fmt.Errorf(i18n.G("script %q doesn't exist in SYSVOL scripts/ subdirectory"), script) + return errors.New(gotext.Get("script %q doesn't exist in SYSVOL scripts/ subdirectory", script)) } if info.IsDir() { - return fmt.Errorf(i18n.G("script %q is a directory and not a file to execute"), script) + return errors.New(gotext.Get("script %q is a directory and not a file to execute", script)) } // nolint:gosec // G302 - scripts need rx permissions if err := os.Chmod(scriptFilePath, 0550); err != nil { - return fmt.Errorf(i18n.G("can't change mode of script %qto %o: %v"), scriptFilePath, 0550, err) + return errors.New(gotext.Get("can't change mode of script %qto %o: %v", scriptFilePath, 0550, err)) } // append it to the list of our scripts @@ -225,7 +225,7 @@ func (m *Manager) ApplyPolicy(ctx context.Context, objectName string, isComputer // RunScripts executes all scripts in directory if ready and not already executed. // allowOrderMissing will not require order to exists if we are ready to execute. func RunScripts(ctx context.Context, order string, allowOrderMissing bool) (err error) { - defer decorate.OnError(&err, i18n.G("can't run scripts listed in %s"), order) + defer decorate.OnError(&err, gotext.Get("can't run scripts listed in %s", order)) log.Infof(ctx, "Calling RunScripts on %q", order) @@ -233,7 +233,7 @@ func RunScripts(ctx context.Context, order string, allowOrderMissing bool) (err // Ensure we are ready to execute if _, err := os.Stat(filepath.Join(baseDir, readyFlag)); err != nil { - return fmt.Errorf(i18n.G("%q is not ready to execute scripts"), order) + return errors.New(gotext.Get("%q is not ready to execute scripts", order)) } // create running flag for the user or machine @@ -273,7 +273,7 @@ func RunScripts(ctx context.Context, order string, allowOrderMissing bool) (err return err } if info.IsDir() { - return fmt.Errorf(i18n.G("%q is a directory and not a file"), order) + return errors.New(gotext.Get("%q is a directory and not a file", order)) } scanner := bufio.NewScanner(f) @@ -300,14 +300,14 @@ func RunScripts(ctx context.Context, order string, allowOrderMissing bool) (err func mkdirAllWithUIDGid(p string, uid, gid int) error { if err := os.MkdirAll(p, 0750); err != nil { - return fmt.Errorf(i18n.G("can't create scripts directory %q: %v"), p, err) + return fmt.Errorf(gotext.Get("can't create scripts directory %q: %v", p, err)) } return chown(p, nil, uid, gid) } func createFlagFile(ctx context.Context, path string, uid, gid int) (err error) { - defer decorate.OnError(&err, i18n.G("can't create flag file %q"), path) + defer decorate.OnError(&err, gotext.Get("can't create flag file %q", path)) log.Debugf(ctx, "Create script flag %q", path) f, err := os.Create(path) @@ -325,7 +325,7 @@ func createFlagFile(ctx context.Context, path string, uid, gid int) (err error) // chown either chown the file descriptor attached, or the path if this one is null to uid and gid. // It will know if we should skip chown for tests. func chown(p string, f *os.File, uid, gid int) (err error) { - defer decorate.OnError(&err, i18n.G("can't chown %q"), p) + defer decorate.OnError(&err, gotext.Get("can't chown %q", p)) if os.Getenv("ADSYS_SKIP_ROOT_CALLS") != "" { uid = -1 diff --git a/internal/stdforward/stdforward.go b/internal/stdforward/stdforward.go index 531f12c82..315e73648 100644 --- a/internal/stdforward/stdforward.go +++ b/internal/stdforward/stdforward.go @@ -8,8 +8,8 @@ import ( "os" "sync" + "github.com/leonelquinteros/gotext" log "github.com/sirupsen/logrus" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" ) @@ -66,7 +66,7 @@ func AddStderrWriter(w io.Writer) (remove func(), err error) { } func addWriter(dest *forwarder, std **os.File, w io.Writer) (f func(), err error) { - defer decorate.OnError(&err, i18n.G("can't redirect output")) + defer decorate.OnError(&err, gotext.Get("can't redirect output")) // Initialize our forwarder var onceErr error diff --git a/internal/systemd/systemd.go b/internal/systemd/systemd.go index 767c8b0bb..2c557d3e9 100644 --- a/internal/systemd/systemd.go +++ b/internal/systemd/systemd.go @@ -8,7 +8,7 @@ import ( systemdDbus "github.com/coreos/go-systemd/v22/dbus" "github.com/godbus/dbus/v5" - "github.com/ubuntu/adsys/internal/i18n" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/decorate" ) @@ -32,7 +32,7 @@ func New(bus *dbus.Conn) (*DefaultCaller, error) { // StartUnit starts the given unit. func (s DefaultCaller) StartUnit(ctx context.Context, unit string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to start unit %s"), unit) + defer decorate.OnError(&err, gotext.Get("failed to start unit %s", unit)) reschan := make(chan string) if _, err = s.conn.StartUnitContext(ctx, unit, "replace", reschan); err != nil { @@ -40,14 +40,14 @@ func (s DefaultCaller) StartUnit(ctx context.Context, unit string) (err error) { } if job := <-reschan; job != jobDone { - return errors.New(i18n.G("start job failed")) + return errors.New(gotext.Get("start job failed")) } return nil } // StopUnit stops the given unit. func (s DefaultCaller) StopUnit(ctx context.Context, unit string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to stop unit %s"), unit) + defer decorate.OnError(&err, gotext.Get("failed to stop unit %s", unit)) reschan := make(chan string) if _, err = s.conn.StopUnitContext(ctx, unit, "replace", reschan); err != nil { @@ -55,14 +55,14 @@ func (s DefaultCaller) StopUnit(ctx context.Context, unit string) (err error) { } if job := <-reschan; job != jobDone { - return errors.New(i18n.G("stop job failed")) + return errors.New(gotext.Get("stop job failed")) } return nil } // EnableUnit enables the given unit. func (s DefaultCaller) EnableUnit(ctx context.Context, unit string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to enable unit %s"), unit) + defer decorate.OnError(&err, gotext.Get("failed to enable unit %s", unit)) if _, _, err := s.conn.EnableUnitFilesContext(ctx, []string{unit}, false, true); err != nil { return err @@ -72,7 +72,7 @@ func (s DefaultCaller) EnableUnit(ctx context.Context, unit string) (err error) // DisableUnit disables the given unit. func (s DefaultCaller) DisableUnit(ctx context.Context, unit string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to disable unit %s"), unit) + defer decorate.OnError(&err, gotext.Get("failed to disable unit %s", unit)) if _, err := s.conn.DisableUnitFilesContext(ctx, []string{unit}, false); err != nil { return err @@ -82,7 +82,7 @@ func (s DefaultCaller) DisableUnit(ctx context.Context, unit string) (err error) // DaemonReload scans and reloads unit files. This is an equivalent to systemctl daemon-reload. func (s DefaultCaller) DaemonReload(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to reload units")) + defer decorate.OnError(&err, gotext.Get("failed to reload units")) return s.conn.ReloadContext(ctx) } diff --git a/internal/watchdservice/watchdservice.go b/internal/watchdservice/watchdservice.go index 160a5f765..52581fbb7 100644 --- a/internal/watchdservice/watchdservice.go +++ b/internal/watchdservice/watchdservice.go @@ -4,15 +4,14 @@ package watchdservice import ( "context" "errors" - "fmt" "os" "strings" "time" "github.com/kardianos/service" + "github.com/leonelquinteros/gotext" watchdconfig "github.com/ubuntu/adsys/internal/config/watchd" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/loghooks" "github.com/ubuntu/adsys/internal/watcher" "github.com/ubuntu/decorate" @@ -124,8 +123,8 @@ func New(ctx context.Context, opts ...option) (*WatchdService, error) { // UpdateDirs updates the watcher with the new directories. func (s *WatchdService) UpdateDirs(ctx context.Context, dirs []string) (err error) { - defer decorate.OnError(&err, i18n.G("failed to change directories to watch")) - log.Info(ctx, i18n.G("Updating directories to watch")) + defer decorate.OnError(&err, gotext.Get("failed to change directories to watch")) + log.Info(ctx, gotext.Get("Updating directories to watch")) if err := s.watcher.UpdateDirs(ctx, dirs); err != nil { return err @@ -138,8 +137,8 @@ func (s *WatchdService) UpdateDirs(ctx context.Context, dirs []string) (err erro // Start starts the watcher service. func (s *WatchdService) Start(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to start service")) - log.Info(ctx, i18n.G("Starting service")) + defer decorate.OnError(&err, gotext.Get("failed to start service")) + log.Info(ctx, gotext.Get("Starting service")) stat, err := s.service.Status() if err != nil { @@ -160,8 +159,8 @@ func (s *WatchdService) Start(ctx context.Context) (err error) { // Stop stops the watcher service. func (s *WatchdService) Stop(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to stop service")) - log.Info(ctx, i18n.G("Stopping service")) + defer decorate.OnError(&err, gotext.Get("failed to stop service")) + log.Info(ctx, gotext.Get("Stopping service")) stat, err := s.service.Status() if err != nil { @@ -204,8 +203,8 @@ func (s *WatchdService) waitForStatus(ctx context.Context, status service.Status // Restart restarts the watcher service. func (s *WatchdService) Restart(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to restart service")) - log.Info(ctx, i18n.G("Restarting service")) + defer decorate.OnError(&err, gotext.Get("failed to restart service")) + log.Info(ctx, gotext.Get("Restarting service")) stat, err := s.service.Status() if err != nil { @@ -228,8 +227,8 @@ func (s *WatchdService) Restart(ctx context.Context) (err error) { // Status provides a status of the watcher service in a pretty format. func (s *WatchdService) Status(ctx context.Context) (status string, err error) { - defer decorate.OnError(&err, i18n.G("failed to retrieve status for service")) - log.Debug(ctx, i18n.G("Getting status from service")) + defer decorate.OnError(&err, gotext.Get("failed to retrieve status for service")) + log.Debug(ctx, gotext.Get("Getting status from service")) uninstalledState := service.Status(42) stat, err := s.service.Status() @@ -242,19 +241,19 @@ func (s *WatchdService) Status(ctx context.Context) (status string, err error) { var serviceStatus string switch stat { case service.StatusRunning: - serviceStatus = i18n.G("running") + serviceStatus = gotext.Get("running") case service.StatusStopped: - serviceStatus = i18n.G("stopped") + serviceStatus = gotext.Get("stopped") case service.StatusUnknown: - serviceStatus = i18n.G("unknown") + serviceStatus = gotext.Get("unknown") case uninstalledState: - serviceStatus = i18n.G("not installed") + serviceStatus = gotext.Get("not installed") default: - serviceStatus = i18n.G("undefined") + serviceStatus = gotext.Get("undefined") } var statStr strings.Builder - statStr.WriteString(fmt.Sprintf(i18n.G("Service status: %s"), serviceStatus)) + statStr.WriteString(gotext.Get("Service status: %s", serviceStatus)) // If the service is installed, attempt to figure out the configured // directories and the binary path. @@ -272,7 +271,7 @@ func (s *WatchdService) Status(ctx context.Context) (status string, err error) { exePath, err = os.Executable() if err != nil { - log.Warningf(ctx, i18n.G("Failed to get current executable path: %v"), err) + log.Warning(ctx, gotext.Get("Failed to get current executable path: %v", err)) } if exePath != svcInfo.binPath && svcInfo.binPath != "" { @@ -281,21 +280,21 @@ func (s *WatchdService) Status(ctx context.Context) (status string, err error) { } statStr.WriteString("\n\n") - statStr.WriteString(fmt.Sprintf(i18n.G("Config file: %s\n"), svcInfo.configFile)) - statStr.WriteString(i18n.G("Watched directories: ")) + statStr.WriteString(gotext.Get("Config file: %s\n", svcInfo.configFile)) + statStr.WriteString(gotext.Get("Watched directories: ")) if len(svcInfo.dirs) == 0 { - statStr.WriteString(i18n.G("no configured directories")) + statStr.WriteString(gotext.Get("no configured directories")) } for _, dir := range svcInfo.dirs { - statStr.WriteString(fmt.Sprintf(i18n.G("\n - %s"), dir)) + statStr.WriteString(gotext.Get("\n - %s", dir)) } if pathMismatch { - log.Warningf(ctx, i18n.G(`Service binary path does not match executable path + log.Warning(ctx, gotext.Get(`Service binary path does not match executable path Service binary path: %s -Current executable path: %s`), svcInfo.binPath, exePath) +Current executable path: %s`, svcInfo.binPath, exePath)) } status = statStr.String() @@ -322,7 +321,7 @@ func (s *WatchdService) ConfigFile(ctx context.Context) (string, error) { // args returns the service configuration extracted from the // service arguments. func (s *WatchdService) args(ctx context.Context) (svcInfo serviceInfo, err error) { - defer decorate.OnError(&err, i18n.G("failed to get service info from arguments")) + defer decorate.OnError(&err, gotext.Get("failed to get service info from arguments")) svcInfo = serviceInfo{} binPath, args, err := s.serviceArgs() @@ -344,8 +343,8 @@ func (s *WatchdService) args(ctx context.Context) (svcInfo serviceInfo, err erro // Install installs the watcher service and starts it if it doesn't // automatically start in due time. func (s *WatchdService) Install(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to install service")) - log.Info(ctx, i18n.G("Installing watcher service")) + defer decorate.OnError(&err, gotext.Get("failed to install service")) + log.Info(ctx, gotext.Get("Installing watcher service")) if err := s.service.Install(); err != nil { return err } @@ -362,12 +361,12 @@ func (s *WatchdService) Install(ctx context.Context) (err error) { // If the service is not installed it logs a message and returns. // If the service is running it attempts to stop it first. func (s *WatchdService) Uninstall(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to uninstall service")) - log.Info(ctx, i18n.G("Uninstalling watcher service")) + defer decorate.OnError(&err, gotext.Get("failed to uninstall service")) + log.Info(ctx, gotext.Get("Uninstalling watcher service")) stat, err := s.service.Status() if errors.Is(err, service.ErrNotInstalled) { - log.Info(ctx, i18n.G("Service is not installed")) + log.Info(ctx, gotext.Get("Service is not installed")) return nil } @@ -405,8 +404,8 @@ func (s *WatchdService) Uninstall(ctx context.Context) (err error) { // Run runs the watcher service. func (s *WatchdService) Run(ctx context.Context) (err error) { - defer decorate.OnError(&err, i18n.G("failed to run service")) + defer decorate.OnError(&err, gotext.Get("failed to run service")) - log.Info(ctx, i18n.G("Running watcher service")) + log.Info(ctx, gotext.Get("Running watcher service")) return s.service.Run() } diff --git a/internal/watchdservice/watchdservice_linux.go b/internal/watchdservice/watchdservice_linux.go index 55de64edd..787f3892b 100644 --- a/internal/watchdservice/watchdservice_linux.go +++ b/internal/watchdservice/watchdservice_linux.go @@ -1,13 +1,14 @@ package watchdservice import ( + "errors" "fmt" "slices" "strings" "github.com/godbus/dbus/v5" + "github.com/leonelquinteros/gotext" "github.com/ubuntu/adsys/internal/consts" - "github.com/ubuntu/adsys/internal/i18n" ) // execStart represents the ExecStart option of a systemd service. It maps to @@ -44,7 +45,7 @@ func (s *WatchdService) serviceArgs() (string, string, error) { var execStarts []execStart err = svcUnit.StoreProperty(fmt.Sprintf("%s.ExecStart", consts.SystemdDbusServiceInterface), &execStarts) if err != nil || len(execStarts) == 0 { - return "", "", fmt.Errorf(i18n.G("could not find %s unit on systemd bus: no service installed? %v"), s.Name(), err) + return "", "", errors.New(gotext.Get("could not find %s unit on systemd bus: no service installed? %v", s.Name(), err)) } binPath := execStarts[0].BinPath diff --git a/internal/watchdtui/watchdtui.go b/internal/watchdtui/watchdtui.go index 05b65dea8..7a02f2dd1 100644 --- a/internal/watchdtui/watchdtui.go +++ b/internal/watchdtui/watchdtui.go @@ -18,7 +18,6 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/leonelquinteros/gotext" watchdconfig "github.com/ubuntu/adsys/internal/config/watchd" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/adsys/internal/watchdservice" ) @@ -165,8 +164,8 @@ func initialModel(configFile string, prevConfigFile string, isDefaultConfig bool switch i { case 0: - t.Placeholder = fmt.Sprintf(i18n.G("Config file location (leave blank for default: %s)"), m.defaultConfig) - t.Prompt = i18n.G("Config file: ") + t.Placeholder = gotext.Get("Config file location (leave blank for default: %s)", m.defaultConfig) + t.Prompt = gotext.Get("Config file: ") t.PromptStyle = boldStyle t.Focus() @@ -176,7 +175,7 @@ func initialModel(configFile string, prevConfigFile string, isDefaultConfig bool t.SetValue(configFile) } case 1: - t.Placeholder = i18n.G("Directory to watch (one per line)") + t.Placeholder = gotext.Get("Directory to watch (one per line)") } m.inputs[i] = t @@ -411,7 +410,7 @@ func updateConfigInputError(input *textinput.Model) { if errors.Is(err, os.ErrNotExist) { input.Err = nil if !filepath.IsAbs(value) { - input.Err = fmt.Errorf(i18n.G("%s will be the absolute path"), absPath) + input.Err = errors.New(gotext.Get("%s will be the absolute path", absPath)) } return } @@ -423,12 +422,12 @@ func updateConfigInputError(input *textinput.Model) { } if stat.IsDir() { - input.Err = fmt.Errorf(i18n.G("%s is a directory; will create %s.yaml inside"), absPath, watchdconfig.CmdName) + input.Err = errors.New(gotext.Get("%s is a directory; will create %s.yaml inside", absPath, watchdconfig.CmdName)) return } if stat.Mode().IsRegular() { - input.Err = fmt.Errorf(i18n.G("%s: file already exists and will be overwritten"), absPath) + input.Err = errors.New(gotext.Get("%s: file already exists and will be overwritten", absPath)) return } @@ -442,7 +441,7 @@ func (m *model) updateDirInputErrorAndStyle(i int) { if m.inputs[i].Value() == "" { m.inputs[i].Err = nil if len(m.inputs) == 2 { - m.inputs[i].Err = errors.New(i18n.G("please enter at least one directory")) + m.inputs[i].Err = errors.New(gotext.Get("please enter at least one directory")) } return } @@ -454,18 +453,18 @@ func (m *model) updateDirInputErrorAndStyle(i int) { m.inputs[i].TextStyle = successStyle if stat, err := os.Stat(absPath); err != nil { - m.inputs[i].Err = fmt.Errorf(i18n.G("%s: directory does not exist, please enter a valid path"), absPath) + m.inputs[i].Err = errors.New(gotext.Get("%s: directory does not exist, please enter a valid path", absPath)) m.inputs[i].TextStyle = noStyle } else if !stat.IsDir() { - m.inputs[i].Err = fmt.Errorf(i18n.G("%s: is not a directory"), absPath) + m.inputs[i].Err = errors.New(gotext.Get("%s: is not a directory", absPath)) m.inputs[i].TextStyle = noStyle } } func (m model) submitText() string { - text := i18n.G("Install") + text := gotext.Get("Install") if m.prevConfig != "" { - text = i18n.G("Update") + text = gotext.Get("Update") } return text } @@ -473,20 +472,20 @@ func (m model) submitText() string { // View renders the UI based on the data in the model. func (m model) View() string { if m.loading { - return fmt.Sprintf(i18n.G("%s installing service... please wait."), m.spinner.View()) + return gotext.Get("%s installing service... please wait.", m.spinner.View()) } if err := m.err; err != nil { - return fmt.Sprintf(i18n.G("Could not install service: %v\n"), err) + return gotext.Get("Could not install service: %v\n", err) } if !m.typing { - return fmt.Sprintln(i18n.G("Service adwatchd was successfully installed and is now running.")) + return fmt.Sprintln(gotext.Get("Service adwatchd was successfully installed and is now running.")) } var b strings.Builder - b.WriteString(titleStyle.Render(i18n.G("Ubuntu AD Watch Daemon Installer"))) + b.WriteString(titleStyle.Render(gotext.Get("Ubuntu AD Watch Daemon Installer"))) b.WriteString("\n\n") // Display config input and hint @@ -501,7 +500,7 @@ func (m model) View() string { b.WriteString("\n\n") - directoriesMsg := i18n.G("Directories:") + directoriesMsg := gotext.Get("Directories:") if m.focusIndex > 0 && m.focusIndex < len(m.inputs) { b.WriteString(focusedStyle.Render(directoriesMsg)) } else { diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index 96a9684a8..425aa40c8 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -6,7 +6,6 @@ package watcher import ( "context" "errors" - "fmt" "os" "path/filepath" "strconv" @@ -15,8 +14,8 @@ import ( "github.com/fsnotify/fsnotify" "github.com/kardianos/service" + "github.com/leonelquinteros/gotext" log "github.com/ubuntu/adsys/internal/grpc/logstreamer" - "github.com/ubuntu/adsys/internal/i18n" "github.com/ubuntu/decorate" "gopkg.in/ini.v1" ) @@ -58,7 +57,7 @@ func init() { // New returns a new Watcher instance. func New(ctx context.Context, initialDirs []string, opts ...option) (*Watcher, error) { if len(initialDirs) == 0 { - return nil, errors.New(i18n.G("no directories to watch")) + return nil, errors.New(gotext.Get("no directories to watch")) } // Set default options @@ -104,7 +103,7 @@ func New(ctx context.Context, initialDirs []string, opts ...option) (*Watcher, e go func() { defer close(watching) if errWatching := w.watch(ctx, dirs, initError); errWatching != nil { - log.Warningf(ctx, i18n.G("Watch failed: %v"), errWatching) + log.Warning(ctx, gotext.Get("Watch failed: %v", errWatching)) } }() err := <-initError @@ -112,7 +111,7 @@ func New(ctx context.Context, initialDirs []string, opts ...option) (*Watcher, e case stopCmd: if watching == nil { - cmdErr <- errors.New(i18n.G("the service is already stopping or not running")) + cmdErr <- errors.New(gotext.Get("the service is already stopping or not running")) break } @@ -140,7 +139,7 @@ func New(ctx context.Context, initialDirs []string, opts ...option) (*Watcher, e // asynchronously. When our function exits, the service manager registers a // signal handler that calls Stop when a signal is received. func (w *Watcher) Start(_ service.Service) (err error) { - defer decorate.OnError(&err, i18n.G("can't start service")) + defer decorate.OnError(&err, gotext.Get("can't start service")) return w.send(nil, startCmd, nil) } @@ -149,7 +148,7 @@ func (w *Watcher) Start(_ service.Service) (err error) { // Documentation states that the function should not take more than a few // seconds to execute. func (w *Watcher) Stop(_ service.Service) (err error) { - defer decorate.OnError(&err, i18n.G("can't stop service")) + defer decorate.OnError(&err, gotext.Get("can't stop service")) return w.send(nil, stopCmd, nil) } @@ -167,21 +166,21 @@ func (w *Watcher) send(ctx *context.Context, action int, dirs []string) error { // UpdateDirs restarts watch loop with new directories. No action is taken if // one or more directories do not exist. func (w *Watcher) UpdateDirs(ctx context.Context, dirs []string) (err error) { - defer decorate.OnError(&err, i18n.G("can't update directories to watch")) - log.Debugf(ctx, i18n.G("Updating directories to %v"), dirs) + defer decorate.OnError(&err, gotext.Get("can't update directories to watch")) + log.Debug(ctx, gotext.Get("Updating directories to %v", dirs)) if len(dirs) == 0 { - return errors.New(i18n.G("need at least one directory to watch")) + return errors.New(gotext.Get("need at least one directory to watch")) } for _, dir := range dirs { if _, err := os.Stat(dir); os.IsNotExist(err) { - return fmt.Errorf(i18n.G("directory %q does not exist"), dir) + return errors.New(gotext.Get("directory %q does not exist", dir)) } } if err := w.send(&ctx, stopCmd, nil); err != nil { - log.Warningf(ctx, i18n.G("Error stopping watcher: %v"), err) + log.Warning(ctx, gotext.Get("Error stopping watcher: %v", err)) } return w.send(&ctx, startCmd, dirs) @@ -189,18 +188,18 @@ func (w *Watcher) UpdateDirs(ctx context.Context, dirs []string) (err error) { // watch is the main watch loop. func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- error) (err error) { - defer decorate.OnError(&err, i18n.G("can't watch over %v"), dirs) + defer decorate.OnError(&err, gotext.Get("can't watch over %v", dirs)) fsWatcher, err := fsnotify.NewWatcher() if err != nil { - initError <- fmt.Errorf(i18n.G("could not initialize fsnotify watcher: %v"), err) + initError <- errors.New(gotext.Get("could not initialize fsnotify watcher: %v", err)) } defer fsWatcher.Close() // Collect directories to watch. for _, dir := range dirs { if err := watchSubDirs(ctx, fsWatcher, dir); err != nil { - initError <- fmt.Errorf(i18n.G("failed to watch directory %q: %v"), dir, err) + initError <- errors.New(gotext.Get("failed to watch directory %q: %v", dir, err)) } } @@ -218,7 +217,7 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err if !ok || event.Name == "" { continue } - log.Debugf(ctx, i18n.G("Got event: %v"), event) + log.Debug(ctx, gotext.Get("Got event: %v", event)) // If the modified file is our own change, ignore it. if strings.EqualFold(filepath.Base(event.Name), gptFileName) { @@ -228,18 +227,18 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err if event.Has(fsnotify.Create) { fileInfo, err := os.Stat(event.Name) if err != nil { - log.Warningf(ctx, i18n.G("Failed to stat: %s"), err) + log.Warning(ctx, gotext.Get("Failed to stat: %s", err)) continue } // Add new detected files and directories to the watch list. if fileInfo.IsDir() { if err := watchSubDirs(ctx, fsWatcher, event.Name); err != nil { - log.Warningf(ctx, i18n.G("Failed to watch: %s"), err) + log.Warning(ctx, gotext.Get("Failed to watch: %s", err)) } } else if fileInfo.Mode().IsRegular() { if err := fsWatcher.Add(event.Name); err != nil { - log.Warningf(ctx, i18n.G("Failed add watcher on %q: %s"), event.Name, err) + log.Warning(ctx, gotext.Get("Failed add watcher on %q: %s", event.Name, err)) } } } @@ -247,7 +246,7 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err // Remove deleted or renamed files/directories from the watch list. if event.Has(fsnotify.Remove) || event.Has(fsnotify.Rename) { if err := fsWatcher.Remove(event.Name); err != nil { - log.Debugf(ctx, i18n.G("Failed to remove watcher on %q: %s"), event.Name, err) + log.Debug(ctx, gotext.Get("Failed to remove watcher on %q: %s", event.Name, err)) } } @@ -289,7 +288,7 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err case err, ok := <-fsWatcher.Errors: if ok { - log.Warningf(ctx, i18n.G("Got event error: %v"), err) + log.Warning(ctx, gotext.Get("Got event error: %v", err)) } continue @@ -298,7 +297,7 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err updateVersions(ctx, modifiedRootDirs) case <-ctx.Done(): - log.Infof(ctx, i18n.G("Watcher stopped")) + log.Infof(ctx, gotext.Get("Watcher stopped")) // Check if there was a timer in progress to not miss an update before exiting. if refreshTimer.Stop() { updateVersions(ctx, modifiedRootDirs) @@ -310,14 +309,14 @@ func (w *Watcher) watch(ctx context.Context, dirs []string, initError chan<- err // watchSubDirs walks a given directory and adds all subdirectories to the watch list. func watchSubDirs(ctx context.Context, fsWatcher *fsnotify.Watcher, path string) (err error) { - defer decorate.OnError(&err, i18n.G("can't watch directory and children of %s"), path) - log.Debugf(ctx, i18n.G("Watching %s and children"), path) + defer decorate.OnError(&err, gotext.Get("can't watch directory and children of %s", path)) + log.Debug(ctx, gotext.Get("Watching %s and children", path)) err = filepath.WalkDir(path, func(p string, d os.DirEntry, err error) error { if err != nil { return err } - log.Debugf(ctx, i18n.G("Watching: %v"), p) + log.Debug(ctx, gotext.Get("Watching: %v", p)) return fsWatcher.Add(p) }) return err @@ -344,7 +343,7 @@ func getRootDir(path string, rootDirs []string) (string, error) { } } if rootDir == "" { - return "", fmt.Errorf(i18n.G("no root directory matching %s found"), path) + return "", errors.New(gotext.Get("no root directory matching %s found", path)) } return rootDir, nil @@ -355,21 +354,21 @@ func updateVersions(ctx context.Context, modifiedRootDirs []string) { for _, dir := range modifiedRootDirs { gptIniPath := filepath.Join(dir, gptFileName) if err := bumpVersion(ctx, gptIniPath); err != nil { - log.Warningf(ctx, i18n.G("Failed to bump %s version: %s"), gptIniPath, err) + log.Warning(ctx, gotext.Get("Failed to bump %s version: %s", gptIniPath, err)) } } } // bumpVersion does the actual bumping of the version in the given GPT.ini file. func bumpVersion(ctx context.Context, path string) (err error) { - defer decorate.OnError(&err, i18n.G("can't bump version for %s"), path) - log.Infof(ctx, i18n.G("Bumping version for %s"), path) + defer decorate.OnError(&err, gotext.Get("can't bump version for %s", path)) + log.Info(ctx, gotext.Get("Bumping version for %s", path)) cfg, err := ini.Load(path) // If the file doesn't exist, create it and initialize the key to be updated. if err != nil { - log.Infof(ctx, i18n.G("error loading ini contents: %v, creating a new file"), err) + log.Info(ctx, gotext.Get("error loading ini contents: %v, creating a new file", err)) cfg = ini.Empty() if _, err := cfg.Section("General").NewKey("Version", "0"); err != nil { return err From 06b6526e0de1317bfabd8e087de9cc2a0cd83e64 Mon Sep 17 00:00:00 2001 From: Didier Roche Date: Tue, 2 Jan 2024 16:10:00 +0100 Subject: [PATCH 2/4] Update go i18n dependencies --- go.mod | 3 +-- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3e27daee9..5b62ab2e8 100644 --- a/go.mod +++ b/go.mod @@ -23,14 +23,13 @@ require ( github.com/mvo5/libsmbclient-go v0.0.0-20220607104205-b69795f58cd0 github.com/pkg/sftp v1.13.6 github.com/sirupsen/logrus v1.9.3 - github.com/snapcore/go-gettext v0.0.0-20191107141714-82bbea49e785 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117 - github.com/ubuntu/go-i18n v0.0.0-20230830081132-d6654b958899 + github.com/ubuntu/go-i18n v0.0.0-20231113092927-594c1754ca47 golang.org/x/crypto v0.17.0 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/net v0.19.0 diff --git a/go.sum b/go.sum index 404c4c478..0f9eeec2e 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/snapcore/go-gettext v0.0.0-20191107141714-82bbea49e785 h1:PaunR+BhraKSLxt2awQ42zofkP+NKh/VjQ0PjIMk/y4= -github.com/snapcore/go-gettext v0.0.0-20191107141714-82bbea49e785/go.mod h1:D3SsWAXK7wCCBZu+Vk5hc1EuKj/L3XN1puEMXTU4LrQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= @@ -275,8 +273,8 @@ github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae h1:vgGSvdW5Lqg+I1 github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae/go.mod h1:quDq6Se6jlGwiIKia/itDZxqC5rj6/8OdFyMMAwTxCs= github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117 h1:XQpsQG5lqRJlx4mUVHcJvyyc1rdTI9nHvwrdfcuy8aM= github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117/go.mod h1:mx0TjbqsaDD9DUT5gA1s3hw47U6RIbbIBfvGzR85K0g= -github.com/ubuntu/go-i18n v0.0.0-20230830081132-d6654b958899 h1:FquZaRc8X0+ImXSHKrqb4hdzcvlCHhXDNLjxzkwit6Y= -github.com/ubuntu/go-i18n v0.0.0-20230830081132-d6654b958899/go.mod h1:ZRhdDyx6YkKz/YiMWi0gS3uMCltgdaKz9IpkiNf/GRg= +github.com/ubuntu/go-i18n v0.0.0-20231113092927-594c1754ca47 h1:CA2dVorxvzdsGtszqhSjyvkrXxZi4bS52ZKvP0Ko634= +github.com/ubuntu/go-i18n v0.0.0-20231113092927-594c1754ca47/go.mod h1:ZRhdDyx6YkKz/YiMWi0gS3uMCltgdaKz9IpkiNf/GRg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From c0e3b541111a2f6b4e38d55afbf23cfd7e06e2aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 06:25:57 +0000 Subject: [PATCH 3/4] deps(go): bump github.com/spf13/viper from 1.17.0 to 1.18.2 Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.2. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.2) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 10 +- go.sum | 433 ++------------------------------------------------------- 2 files changed, 17 insertions(+), 426 deletions(-) diff --git a/go.mod b/go.mod index 5b62ab2e8..624168543 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.17.0 + github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 github.com/termie/go-shutil v0.0.0-20140729215957-bcacb06fecae github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117 @@ -72,16 +72,16 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.10.0 // indirect - github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/yuin/goldmark v1.5.2 // indirect github.com/yuin/goldmark-emoji v1.0.1 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/term v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect ) diff --git a/go.sum b/go.sum index 0f9eeec2e..6504d5200 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= @@ -47,7 +7,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= @@ -56,13 +15,6 @@ github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM2 github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -75,104 +27,33 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -222,46 +103,40 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= -github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= -github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -275,166 +150,38 @@ github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117 h1:XQpsQG5lqRJlx4m github.com/ubuntu/decorate v0.0.0-20230125165522-2d5b0a9bb117/go.mod h1:mx0TjbqsaDD9DUT5gA1s3hw47U6RIbbIBfvGzR85K0g= github.com/ubuntu/go-i18n v0.0.0-20231113092927-594c1754ca47 h1:CA2dVorxvzdsGtszqhSjyvkrXxZi4bS52ZKvP0Ko634= github.com/ubuntu/go-i18n v0.0.0-20231113092927-594c1754ca47/go.mod h1:ZRhdDyx6YkKz/YiMWi0gS3uMCltgdaKz9IpkiNf/GRg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -450,189 +197,33 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 8c7934326709cca9d34458b35c9ad7a59b5693c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 08:30:52 +0000 Subject: [PATCH 4/4] deps(go): bump google.golang.org/grpc from 1.60.0 to 1.60.1 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.60.0 to 1.60.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.60.0...v1.60.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 624168543..482225f8c 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( golang.org/x/sync v0.5.0 golang.org/x/sys v0.15.0 golang.org/x/text v0.14.0 - google.golang.org/grpc v1.60.0 + google.golang.org/grpc v1.60.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf v1.31.0 gopkg.in/ini.v1 v1.67.0 diff --git a/go.sum b/go.sum index 6504d5200..df90fa9f2 100644 --- a/go.sum +++ b/go.sum @@ -211,8 +211,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= -google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=