Skip to content

Commit 231ca29

Browse files
authored
Merge pull request #4268 from saschagrunert/gmail-oauth-announce
Replace SendGrid with Gmail OAuth for release announcements
2 parents a64fccf + c06bbc7 commit 231ca29

File tree

11 files changed

+783
-787
lines changed

11 files changed

+783
-787
lines changed

cmd/krel/cmd/announce_send.go

Lines changed: 27 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,23 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
2223

2324
"github.com/sirupsen/logrus"
2425
"github.com/spf13/cobra"
2526

26-
"sigs.k8s.io/release-utils/env"
2727
"sigs.k8s.io/release-utils/helpers"
2828
"sigs.k8s.io/release-utils/http"
2929

3030
"k8s.io/release/pkg/mail"
3131
"k8s.io/release/pkg/release"
3232
)
3333

34-
const (
35-
sendgridAPIKeyEnvKey = "SENDGRID_API_KEY" //nolint:gosec // it's just the key
36-
nameFlag = "name"
37-
emailFlag = "email"
38-
)
34+
const noBrowserFlag = "no-browser"
3935

40-
// announceCmd represents the subcommand for `krel announce`.
36+
// sendAnnounceCmd represents the subcommand for `krel announce send`.
4137
var sendAnnounceCmd = &cobra.Command{
4238
Use: "send",
4339
Short: "Announce Kubernetes releases",
@@ -47,28 +43,26 @@ krel announce send can be used to mail an announcement of an already
4743
built Kubernetes release to the %q and %q Google Groups.
4844
4945
By default the mail will be sent only to a test Google Group %q,
50-
ie: the announcement run will only be a mock run. To do an
46+
ie: the announcement run will only be a mock run. To do an
5147
official announcement, use the --nomock flag.
5248
53-
It is necessary to export the $%s environment variable. An API key can be created by
54-
registering a sendgrid.com account and adding the key here:
55-
56-
https://app.sendgrid.com/settings/api_keys
49+
Email is sent via the Gmail API using Google OAuth. A browser window
50+
will open for authorization on each run. No additional setup is
51+
needed.
5752
58-
Beside this, if the flags for a valid sender name (--%s,-n) and sender email
59-
address (--%s,-e) are not set, then it tries to retrieve those values directly
60-
from the Sendgrid API.
53+
Use --%s to print the authorization URL for manual copy/paste
54+
(useful in headless environments). After authorizing in the browser,
55+
the redirect will fail to load. Copy the full URL from the browser's
56+
address bar and paste it back into the terminal.
6157
6258
Setting a valid Kubernetes tag (--%s,-t) is always necessary.
6359
64-
If --%s,-p is given, then krel announce will only print the email content
65-
without doing anything else.`,
60+
If --%s,-p is given, then krel announce will only print the email
61+
content without doing anything else.`,
6662
mail.KubernetesAnnounceGoogleGroup,
6763
mail.KubernetesDevGoogleGroup,
6864
mail.KubernetesAnnounceTestGoogleGroup,
69-
sendgridAPIKeyEnvKey,
70-
nameFlag,
71-
emailFlag,
65+
noBrowserFlag,
7266
tagFlag,
7367
printOnlyFlag,
7468
),
@@ -80,30 +74,17 @@ without doing anything else.`,
8074
}
8175

8276
type sendAnnounceOptions struct {
83-
sendgridAPIKey string
84-
name string
85-
email string
77+
noBrowser bool
8678
}
8779

8880
var sendAnnounceOpts = &sendAnnounceOptions{}
8981

9082
func init() {
91-
sendAnnounceOpts.sendgridAPIKey = env.Default(sendgridAPIKeyEnvKey, "")
92-
93-
sendAnnounceCmd.PersistentFlags().StringVarP(
94-
&sendAnnounceOpts.name,
95-
nameFlag,
96-
"n",
97-
"",
98-
"mail sender name",
99-
)
100-
101-
sendAnnounceCmd.PersistentFlags().StringVarP(
102-
&sendAnnounceOpts.email,
103-
emailFlag,
104-
"e",
105-
"",
106-
"email address",
83+
sendAnnounceCmd.PersistentFlags().BoolVar(
84+
&sendAnnounceOpts.noBrowser,
85+
noBrowserFlag,
86+
false,
87+
"disable automatic browser opening for OAuth (manual URL copy/paste)",
10788
)
10889

10990
announceCmd.AddCommand(sendAnnounceCmd)
@@ -126,7 +107,7 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
126107
content, err := http.NewAgent().Get(u)
127108
if err != nil {
128109
return fmt.Errorf(
129-
"unable to retrieve release announcement form url: %s: %w", u, err,
110+
"unable to retrieve release announcement from url: %s: %w", u, err,
130111
)
131112
}
132113

@@ -137,26 +118,11 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
137118
return nil
138119
}
139120

140-
if opts.sendgridAPIKey == "" {
141-
return fmt.Errorf(
142-
"$%s is not set", sendgridAPIKeyEnvKey,
143-
)
144-
}
145-
146-
logrus.Info("Preparing mail sender")
147-
148-
m := mail.NewSender(opts.sendgridAPIKey)
149-
150-
if opts.name != "" && opts.email != "" {
151-
if err := m.SetSender(opts.name, opts.email); err != nil {
152-
return fmt.Errorf("unable to set mail sender: %w", err)
153-
}
154-
} else {
155-
logrus.Info("Retrieving default sender from sendgrid API")
121+
logrus.Info("Starting Gmail OAuth flow")
156122

157-
if err := m.SetDefaultSender(); err != nil {
158-
return fmt.Errorf("setting default sender: %w", err)
159-
}
123+
sender, err := mail.NewGmailSender(context.Background(), opts.noBrowser)
124+
if err != nil {
125+
return fmt.Errorf("creating Gmail sender: %w", err)
160126
}
161127

162128
groups := []mail.GoogleGroup{mail.KubernetesAnnounceTestGoogleGroup}
@@ -169,9 +135,7 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
169135

170136
logrus.Infof("Using Google Groups as announcement target: %v", groups)
171137

172-
if err := m.SetGoogleGroupRecipients(groups...); err != nil {
173-
return fmt.Errorf("unable to set mail recipients: %w", err)
174-
}
138+
sender.SetGoogleGroupRecipients(groups...)
175139

176140
logrus.Info("Sending mail")
177141

@@ -187,7 +151,7 @@ func runAnnounce(opts *sendAnnounceOptions, announceRootOpts *announceOptions, r
187151
}
188152

189153
if yes {
190-
if err := m.Send(string(content), subject); err != nil {
154+
if err := sender.Send(string(content), subject); err != nil {
191155
return fmt.Errorf("unable to send mail: %w", err)
192156
}
193157
}

docs/krel/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,49 @@ krel has several subcommands that perform various tasks during the release lifec
5454
| stage | Stage a new Kubernetes version |
5555
| testgridshot | Take a screenshot of the testgrid dashboards |
5656

57+
## Sending Release Announcements
58+
59+
The `krel announce send` command sends release announcement emails via the
60+
Gmail API using Google OAuth. No additional setup is required — a browser
61+
window will open for authorization on each run.
62+
63+
In mock mode (default), emails are sent to
64+
[kubernetes-announce-test](https://groups.google.com/g/kubernetes-announce-test).
65+
In production mode (`--nomock`), emails are sent to
66+
[kubernetes-announce](https://groups.google.com/g/kubernetes-announce) and
67+
[dev@kubernetes.io](https://groups.google.com/a/kubernetes.io/g/dev).
68+
69+
```shell
70+
# Mock run (sends to kubernetes-announce-test only):
71+
krel announce send --tag v1.35.1
72+
73+
# Production run (sends to kubernetes-announce and dev):
74+
krel announce send --tag v1.35.1 --nomock
75+
76+
# Print announcement content without sending:
77+
krel announce send --tag v1.35.1 --print-only
78+
```
79+
80+
### Headless environments (`--no-browser`)
81+
82+
If you are running `krel` in a headless environment (e.g. SSH session), use the
83+
`--no-browser` flag:
84+
85+
```shell
86+
krel announce send --tag v1.35.1 --no-browser
87+
```
88+
89+
This prints the OAuth authorization URL to the terminal instead of opening a
90+
browser. Open the URL in any browser (can be on another machine), authorize the
91+
application, and the browser will redirect to `http://localhost?code=...`. Since
92+
no local server is listening, the page will fail to load — this is expected.
93+
Copy the full URL from the browser's address bar and paste it back into the
94+
terminal to complete authentication.
95+
96+
The OAuth app is managed in the
97+
[k8s-release](https://console.cloud.google.com/auth/clients?project=k8s-release)
98+
Google Cloud project.
99+
57100
## Important Notes
58101

59102
Some of the krel subcommands are under development and their usage may already differ from these docs.

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ require (
2121
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481
2222
github.com/psampaz/go-mod-outdated v0.9.0
2323
github.com/saschagrunert/go-modiff v1.3.5
24-
github.com/sendgrid/rest v2.6.9+incompatible
25-
github.com/sendgrid/sendgrid-go v3.16.1+incompatible
2624
github.com/sergi/go-diff v1.4.0
2725
github.com/shirou/gopsutil/v3 v3.24.5
2826
github.com/shurcooL/githubv4 v0.0.0-20220115235240-a14260e6f8a2

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,10 +801,6 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL
801801
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
802802
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
803803
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
804-
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
805-
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
806-
github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1GjJohAA0p6hVEaDtHWWs=
807-
github.com/sendgrid/sendgrid-go v3.16.1+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
808804
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
809805
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
810806
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=

0 commit comments

Comments
 (0)