diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4fc436f6..3914a447 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ The CLI follows a resource-oriented architecture: - **Resources** (`internal/resource/`) — Unified interface for API objects with field introspection - **Multi-Metro** (`internal/multimetro/`) — Client abstraction for global infrastructure operations - **Configuration** (`internal/config/`) — Profile and credential management -- **Telemetry** (`internal/telemetry/`) — Anonymous usage analytics (opt-out via `--no-telemetry`) +- **Telemetry** (`internal/telemetry/`) — Usage analytics (opt-out via `--no-telemetry`) ### Key Dependencies diff --git a/README.md b/README.md index 9e608e35..7ba5a58e 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ The CLI stores configuration in `~/.config/unikraft/config.yaml` (or the path sp | `UNIKRAFT_PROFILE_ENV` | Use a virtual profile from `UKC_*` env vars instead of config (`true`, `false`) | | `UNIKRAFT_LOG_LEVEL` | Set log level (`trace`, `debug`, `info`, `warn`, `error`, `fatal`) | | `UNIKRAFT_LOG_TYPE` | Set output format (`text`, `json`) | -| `UNIKRAFT_TELEMETRY` | Enable/disable anonymous telemetry (`true`, `false`) | +| `UNIKRAFT_TELEMETRY` | Enable/disable usage analytics (`true`, `false`) | | `UKC_TOKEN` | API token for legacy profiles / `UNIKRAFT_PROFILE_ENV` | | `UKC_METRO` | Metro name (e.g. `fra`) or full endpoint URL for legacy profiles / `UNIKRAFT_PROFILE_ENV` | | `UKC_ALLOW_INSECURE` | Skip TLS verification for the metro from `UKC_METRO` (`true`, `false`) | @@ -417,8 +417,9 @@ unikraft completion -c powershell > unikraft.ps1 ## Telemetry -The CLI collects anonymous usage analytics to improve the product. -No personally identifiable information is collected. +The CLI collects usage analytics to improve the product. +When you are logged in, events are keyed to the user and organization UUIDs of the active profile. +Otherwise, events use an anonymous machine hash. To opt out: ```sh diff --git a/cmd/unikraft/main.go b/cmd/unikraft/main.go index 378ec884..df5f5dbe 100644 --- a/cmd/unikraft/main.go +++ b/cmd/unikraft/main.go @@ -154,12 +154,12 @@ func run(ctx context.Context, args []string, stdio config.Stdio) (context.Contex // Initialize analytics if telemetry is enabled. // - // These are anonymous usage analytics, and no personally identifiable - // information is collected. This information is used to help us understand - // how the CLI is being used and to improve it over time. We may collect - // information such as which commands are used, how often they are used, and - // any errors that occur. This data is aggregated and analyzed to identify - // trends and areas for improvement. + // These usage analytics are keyed to the logged-in account when there is + // one, and anonymous otherwise. This information is used to help us + // understand how the CLI is being used and to improve it over time. We may + // collect information such as which commands are used, how often they are + // used, and any errors that occur. This data is aggregated and analyzed to + // identify trends and areas for improvement. // // Unikraft is committed to user privacy and data protection; visit[0] for // more information. @@ -173,7 +173,8 @@ func run(ctx context.Context, args []string, stdio config.Stdio) (context.Contex // for our CLI. _, doNotTrack := os.LookupEnv("DO_NOT_TRACK") if !doNotTrack && opts.Telemetry && !isSendAnalytics { - if err := telemetry.Init(); err != nil { + profile, _ := config.G(ctx).CurrentProfile() + if err := telemetry.Init(profile); err != nil { log.G(ctx). Debug(). Err(err). @@ -181,7 +182,7 @@ func run(ctx context.Context, args []string, stdio config.Stdio) (context.Contex } else { log.G(ctx). Debug(). - Msg("collecting anonymous usage analytics, set `UNIKRAFT_TELEMETRY=false` to disable") + Msg("collecting usage analytics, set `UNIKRAFT_TELEMETRY=false` to disable") } } diff --git a/cmd/unikraft/testdata/TestHelp/api b/cmd/unikraft/testdata/TestHelp/api index 6f90cb5a..b6f6988d 100644 --- a/cmd/unikraft/testdata/TestHelp/api +++ b/cmd/unikraft/testdata/TestHelp/api @@ -199,7 +199,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/auth b/cmd/unikraft/testdata/TestHelp/auth index d4554e1c..d0becdeb 100644 --- a/cmd/unikraft/testdata/TestHelp/auth +++ b/cmd/unikraft/testdata/TestHelp/auth @@ -34,7 +34,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -60,7 +60,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -99,7 +99,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -146,7 +146,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -199,7 +199,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -232,7 +232,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -287,7 +287,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -352,7 +352,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -429,7 +429,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -461,7 +461,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/build b/cmd/unikraft/testdata/TestHelp/build index 43b94e84..d91e06cc 100644 --- a/cmd/unikraft/testdata/TestHelp/build +++ b/cmd/unikraft/testdata/TestHelp/build @@ -60,7 +60,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/certificates b/cmd/unikraft/testdata/TestHelp/certificates index 4bcb6af3..2a5a19ef 100644 --- a/cmd/unikraft/testdata/TestHelp/certificates +++ b/cmd/unikraft/testdata/TestHelp/certificates @@ -45,7 +45,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -101,7 +101,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -163,7 +163,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -219,7 +219,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -288,7 +288,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -363,7 +363,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/config b/cmd/unikraft/testdata/TestHelp/config index 63926df0..091d5b46 100644 --- a/cmd/unikraft/testdata/TestHelp/config +++ b/cmd/unikraft/testdata/TestHelp/config @@ -13,10 +13,11 @@ Fields: path profile profiles, profiles.*, profiles.*.name, profiles.*.type, profiles.*.token, - profiles.*.organization, profiles.*.control-plane, profiles.*.insecure, - profiles.*.metros, profiles.*.metros.*, profiles.*.metros.*.name, - profiles.*.metros.*.endpoint, profiles.*.metros.*.location, - profiles.*.metros.*.insecure, profiles.*.metro-default + profiles.*.organization, profiles.*.user-uuid, profiles.*.organization-uuid, + profiles.*.control-plane, profiles.*.insecure, profiles.*.metros, + profiles.*.metros.*, profiles.*.metros.*.name, profiles.*.metros.*.endpoint, + profiles.*.metros.*.location, profiles.*.metros.*.insecure, profiles.*.metro- + default Global flags: -h, --help @@ -32,7 +33,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -56,10 +57,11 @@ Fields: path profile profiles, profiles.*, profiles.*.name, profiles.*.type, profiles.*.token, - profiles.*.organization, profiles.*.control-plane, profiles.*.insecure, - profiles.*.metros, profiles.*.metros.*, profiles.*.metros.*.name, - profiles.*.metros.*.endpoint, profiles.*.metros.*.location, - profiles.*.metros.*.insecure, profiles.*.metro-default + profiles.*.organization, profiles.*.user-uuid, profiles.*.organization-uuid, + profiles.*.control-plane, profiles.*.insecure, profiles.*.metros, + profiles.*.metros.*, profiles.*.metros.*.name, profiles.*.metros.*.endpoint, + profiles.*.metros.*.location, profiles.*.metros.*.insecure, profiles.*.metro- + default Flags: -w, --watch= @@ -83,7 +85,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/general b/cmd/unikraft/testdata/TestHelp/general index 8686698c..8ee66168 100644 --- a/cmd/unikraft/testdata/TestHelp/general +++ b/cmd/unikraft/testdata/TestHelp/general @@ -86,7 +86,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -188,7 +188,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -291,7 +291,7 @@ Examples: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -392,7 +392,7 @@ Examples: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/images b/cmd/unikraft/testdata/TestHelp/images index 0dca8ca7..bb6cc8a2 100644 --- a/cmd/unikraft/testdata/TestHelp/images +++ b/cmd/unikraft/testdata/TestHelp/images @@ -36,7 +36,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -104,7 +104,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -160,7 +160,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -218,7 +218,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -265,7 +265,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/instances b/cmd/unikraft/testdata/TestHelp/instances index 41aead5a..ef718009 100644 --- a/cmd/unikraft/testdata/TestHelp/instances +++ b/cmd/unikraft/testdata/TestHelp/instances @@ -104,7 +104,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -185,7 +185,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -272,7 +272,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -353,7 +353,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -464,7 +464,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -661,7 +661,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -869,7 +869,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1065,7 +1065,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1196,7 +1196,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1257,7 +1257,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1322,7 +1322,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1393,7 +1393,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1466,7 +1466,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1549,7 +1549,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1630,7 +1630,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1694,7 +1694,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1758,7 +1758,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1828,7 +1828,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1892,7 +1892,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1964,7 +1964,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2046,7 +2046,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2126,7 +2126,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2166,7 +2166,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2215,7 +2215,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2260,7 +2260,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2311,7 +2311,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2357,7 +2357,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2410,7 +2410,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2463,7 +2463,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2503,7 +2503,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2563,7 +2563,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2626,7 +2626,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2680,7 +2680,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -2737,7 +2737,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/resources b/cmd/unikraft/testdata/TestHelp/resources index a5ea6f56..3c9a1539 100644 --- a/cmd/unikraft/testdata/TestHelp/resources +++ b/cmd/unikraft/testdata/TestHelp/resources @@ -35,7 +35,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -82,7 +82,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/run b/cmd/unikraft/testdata/TestHelp/run index 44a662f8..86fce8aa 100644 --- a/cmd/unikraft/testdata/TestHelp/run +++ b/cmd/unikraft/testdata/TestHelp/run @@ -114,7 +114,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/services b/cmd/unikraft/testdata/TestHelp/services index 39bb2047..efd8aae3 100644 --- a/cmd/unikraft/testdata/TestHelp/services +++ b/cmd/unikraft/testdata/TestHelp/services @@ -48,7 +48,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -105,7 +105,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -168,7 +168,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -225,7 +225,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -290,7 +290,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -382,7 +382,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -462,7 +462,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/cmd/unikraft/testdata/TestHelp/volumes b/cmd/unikraft/testdata/TestHelp/volumes index ad856368..d0cee107 100644 --- a/cmd/unikraft/testdata/TestHelp/volumes +++ b/cmd/unikraft/testdata/TestHelp/volumes @@ -66,7 +66,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -129,7 +129,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -198,7 +198,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -261,7 +261,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -331,7 +331,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -397,7 +397,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -454,7 +454,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -496,7 +496,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -545,7 +545,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -620,7 +620,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -701,7 +701,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -752,7 +752,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -807,7 +807,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -868,7 +868,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -931,7 +931,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -998,7 +998,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). @@ -1065,7 +1065,7 @@ Global flags: --profile= ($UNIKRAFT_PROFILE) Set the current profile. --[no-]telemetry ($UNIKRAFT_TELEMETRY) - Toggle anonymous usage analytics. + Toggle usage analytics. [default: true] --timeout= ($UNIKRAFT_TIMEOUT) Set a deadline for the command (e.g. 30s, 5m, 1h). diff --git a/internal/cmd/login/login.go b/internal/cmd/login/login.go index 23b9535c..360d1d47 100644 --- a/internal/cmd/login/login.go +++ b/internal/cmd/login/login.go @@ -61,7 +61,7 @@ func (cmd *LoginCmd) Run(ctx context.Context, cfg *config.Config) error { } // Get the token either from file or via browser authentication - var token, organization string + var token, organization, userUUID, orgUUID string if cmd.Token != nil { log.G(ctx).Info(). Msg("reading authentication token from file") @@ -88,20 +88,30 @@ func (cmd *LoginCmd) Run(ctx context.Context, cfg *config.Config) error { } token = resp.Data.Token organization = cmp.Or(resp.Data.OrganizationName, cmd.Organization) + userUUID = resp.Data.UserUuid + orgUUID = resp.Data.OrganizationUuid } tempProfile.Token = token - if organization == "" { - var err error - organization, err = cmd.getOrg(ctx, tempProfile) - if err != nil { + if orgUUID == "" || organization == "" { + auth, err := cmd.getAuthorization(ctx, tempProfile) + switch { + case err != nil && organization == "": log.G(ctx).Error(). Msg("could not determine organization from control plane, specify organization with --organization flag") return err - } else { - log.G(ctx).Info(). - Str("organization", organization). - Msg("found organization from control plane") + case err != nil: + log.G(ctx).Warn(). + Err(err). + Msg("could not fetch organization details from control plane") + default: + if organization == "" { + log.G(ctx).Info(). + Str("organization", auth.OrganizationName). + Msg("found organization from control plane") + } + organization = cmp.Or(organization, auth.OrganizationName) + orgUUID = auth.OrganizationUuid } } @@ -120,6 +130,12 @@ func (cmd *LoginCmd) Run(ctx context.Context, cfg *config.Config) error { } profile.Token = token profile.Organization = organization + if userUUID != "" { + profile.UserUUID = userUUID + } + if orgUUID != "" { + profile.OrganizationUUID = orgUUID + } profile.ControlPlane = loginControlPlane profile.Insecure = cmd.AllowInsecure @@ -293,22 +309,22 @@ func (cmd *LoginCmd) getAuth(ctx context.Context, profile *config.Profile) (*con } } -func (cmd *LoginCmd) getOrg(ctx context.Context, profile *config.Profile) (string, error) { +func (cmd *LoginCmd) getAuthorization(ctx context.Context, profile *config.Profile) (*controlplane.GetAuthorizationResponseData, error) { log.G(ctx).Trace(). Str("controlplane", profile.ControlPlane). Msg("fetching organization") client, err := multimetro.NewControlClientFromProfile(profile) if err != nil { - return "", err + return nil, err } resp, err := client.GetAuthorization(ctx) if err != nil { - return "", jujuerrors.Annotate(err, "getting authorization") + return nil, jujuerrors.Annotate(err, "getting authorization") } if resp.Data == nil || resp.Data.OrganizationName == "" { - return "", jujuerrors.New("no organization name received from control plane") + return nil, jujuerrors.New("no organization name received from control plane") } - return resp.Data.OrganizationName, nil + return resp.Data, nil } diff --git a/internal/cmd/root.go b/internal/cmd/root.go index ddb58f97..2030b153 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -110,7 +110,7 @@ type globalFlags struct { Profile string `group:"flag-global" name:"profile" env:"UNIKRAFT_PROFILE" help:"Set the current profile." placeholder:"name"` - Telemetry bool `group:"flag-global" name:"telemetry" env:"UNIKRAFT_TELEMETRY" help:"Toggle anonymous usage analytics." default:"true" negatable:""` + Telemetry bool `group:"flag-global" name:"telemetry" env:"UNIKRAFT_TELEMETRY" help:"Toggle usage analytics." default:"true" negatable:""` Timeout time.Duration `group:"flag-global" name:"timeout" env:"UNIKRAFT_TIMEOUT" help:"Set a deadline for the command (e.g. 30s, 5m, 1h)." placeholder:"duration" optional:""` } diff --git a/internal/config/config.go b/internal/config/config.go index f338c8a1..54738eb8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -68,18 +68,37 @@ func (c *Config) Save() error { if err := os.MkdirAll(filepath.Dir(c.Path), 0o755); err != nil { return jujuerrors.Annotate(err, "creating config directory") } - f, err := os.Create(c.Path) + + mode := os.FileMode(0o600) + if info, err := os.Stat(c.Path); err == nil { + mode = info.Mode().Perm() + } + + f, err := os.CreateTemp(filepath.Dir(c.Path), "."+filepath.Base(c.Path)+".*") if err != nil { - return jujuerrors.Annotate(err, "opening config file") + return jujuerrors.Annotate(err, "creating temporary config file") } + tmp := f.Name() + defer os.Remove(tmp) if _, err := f.Write(updated); err != nil { f.Close() return jujuerrors.Annotate(err, "writing config file") } + if err := f.Sync(); err != nil { + f.Close() + return jujuerrors.Annotate(err, "syncing config file") + } if err := f.Close(); err != nil { return jujuerrors.Annotate(err, "closing config file") } + + if err := os.Chmod(tmp, mode); err != nil { + return jujuerrors.Annotate(err, "setting config file permissions") + } + if err := os.Rename(tmp, c.Path); err != nil { + return jujuerrors.Annotate(err, "replacing config file") + } return nil } diff --git a/internal/config/profile.go b/internal/config/profile.go index 8b5f7a46..df8555a2 100644 --- a/internal/config/profile.go +++ b/internal/config/profile.go @@ -73,6 +73,10 @@ type Profile struct { Token string `json:"token,omitempty" field:",long"` // Organization is the organization associated with the profile. Organization string `json:"organization,omitempty" field:",short"` + // UserUUID is the UUID of the user who approved the login, if known. + UserUUID string `json:"user_uuid,omitempty" field:",long"` + // OrganizationUUID is the UUID of the organization of the token. + OrganizationUUID string `json:"organization_uuid,omitempty" field:",long"` // ControlPlane is the endpoint for the control plane associated with the profile. ControlPlane string `json:"controlplane,omitempty" field:",long"` // Insecure indicates whether to allow insecure connections to the control plane, skipping TLS verification. diff --git a/internal/telemetry/detach.go b/internal/telemetry/detach.go index b91ea9f3..078f1088 100644 --- a/internal/telemetry/detach.go +++ b/internal/telemetry/detach.go @@ -26,7 +26,19 @@ func spawnDetachedAnalytics(event posthog.Capture) { return } - payload, err := json.Marshal(event.APIfy()) + event.Properties.Set("machine_id", machineID) + if anonymous { + event.Properties.Set("$process_person_profile", false) + } else if userless { + event.Properties.Set("$set", posthog.Properties{"userless": true}) + } + + payload, err := json.Marshal(EventPayload{ + Event: event.Event, + DistinctID: event.DistinctId, + Properties: event.Properties, + Groups: event.Groups, + }) if err != nil { return } diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index 53d6b145..3b896af6 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -22,6 +22,8 @@ import ( "unikraft.com/x/fingerprint" "unikraft.com/x/version" + + "unikraft.com/cli/internal/config" ) var ( @@ -36,9 +38,13 @@ var ( var ( distinctID string + machineID string sessionID string + groups posthog.Groups enabled bool mu sync.Mutex + anonymous bool + userless bool // commandStart tracks when the current command started for duration calculation. commandStart time.Time @@ -53,13 +59,13 @@ var ( type EventPayload struct { Event string `json:"event"` DistinctID string `json:"distinct_id"` - SessionID string `json:"session_id"` Properties map[string]any `json:"properties"` - Timestamp time.Time `json:"timestamp"` + Groups posthog.Groups `json:"groups,omitempty"` } -// Init initializes the PostHog analytics client. -func Init() error { +// Init initializes the PostHog analytics client for the given profile. +// If profile is nil, Init uses the anonymous machine fingerprint. +func Init(profile *config.Profile) error { mu.Lock() defer mu.Unlock() @@ -74,8 +80,26 @@ func Init() error { return fmt.Errorf("no API key set for PostHog; use UNIKRAFT_POSTHOG_API_KEY environment variable") } - // Generate anonymous distinct ID from machine fingerprint - distinctID = generateDistinctID() + // Use the user UUID when known, otherwise the machine fingerprint. + machineID = generateMachineID() + distinctID = machineID + groups = nil + anonymous = true + userless = true + if profile != nil { + if profile.OrganizationUUID != "" { + groups = posthog.Groups{"organization": profile.OrganizationUUID} + } + switch { + case profile.UserUUID != "": + distinctID = profile.UserUUID + anonymous = false + userless = false + case profile.OrganizationUUID != "": + distinctID = profile.OrganizationUUID + anonymous = false + } + } // Generate unique session ID for this CLI invocation. sessionID = generateSessionID() @@ -83,9 +107,9 @@ func Init() error { return nil } -// generateDistinctID creates an anonymous distinct ID based on machine fingerprint. +// generateMachineID creates an anonymous ID from the machine fingerprint. // The ID is a SHA-256 hash to ensure privacy while maintaining consistency. -func generateDistinctID() string { +func generateMachineID() string { fp, err := fingerprint.New() if err != nil { // Fallback to hostname-based ID @@ -106,7 +130,7 @@ func generateDistinctID() string { return hex.EncodeToString(hash[:16]) } -// generateDistinctID creates a unique session ID for this CLI invocation, which +// generateSessionID creates a unique session ID for this CLI invocation, which // can be used to group events together. func generateSessionID() string { buf := make([]byte, 8) @@ -175,6 +199,6 @@ func SendEvent(payloadJSON string) error { DistinctId: payload.DistinctID, Event: payload.Event, Properties: props, - Timestamp: payload.Timestamp, + Groups: payload.Groups, }) } diff --git a/internal/telemetry/track.go b/internal/telemetry/track.go index db387748..bf16a120 100644 --- a/internal/telemetry/track.go +++ b/internal/telemetry/track.go @@ -35,6 +35,7 @@ func TrackCrash(panicValue any, stack []byte) { DistinctId: distinctID, Event: "cli_crash", Properties: props, + Groups: groups, }) } @@ -75,6 +76,7 @@ func TrackCommandStart(cmdPath string) { Properties: posthog.NewProperties(). Set("command", cmdPath). Set("session_id", sessionID), + Groups: groups, }) } @@ -96,6 +98,7 @@ func TrackCommandSuccess(cmdPath string) { Set("command", cmdPath). Set("duration_ms", duration.Milliseconds()). Set("session_id", sessionID), + Groups: groups, }) } @@ -127,5 +130,6 @@ func TrackCommandError(cmdPath string, err error) { DistinctId: distinctID, Event: "command_failed", Properties: props, + Groups: groups, }) }