Skip to content

Commit 32c376e

Browse files
committed
Merge remote-tracking branch 'origin/main' into spog-fixes
# Conflicts: # connector.go # telemetry/config.go # telemetry/config_test.go # telemetry/driver_integration.go # telemetry/featureflag.go # telemetry/featureflag_test.go
2 parents 6bda425 + 3f115aa commit 32c376e

73 files changed

Lines changed: 3813 additions & 3691 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Setup JFrog OIDC
2+
description: Obtain a JFrog access token via GitHub OIDC and configure Go to use JFrog as a module proxy
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- name: Get JFrog OIDC token
8+
shell: bash
9+
run: |
10+
set -euo pipefail
11+
ID_TOKEN=$(curl -sLS \
12+
-H "User-Agent: actions/oidc-client" \
13+
-H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
14+
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=jfrog-github" | jq .value | tr -d '"')
15+
echo "::add-mask::${ID_TOKEN}"
16+
ACCESS_TOKEN=$(curl -sLS -XPOST -H "Content-Type: application/json" \
17+
"https://databricks.jfrog.io/access/api/v1/oidc/token" \
18+
-d "{\"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\", \"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\", \"subject_token\": \"${ID_TOKEN}\", \"provider_name\": \"github-actions\"}" | jq .access_token | tr -d '"')
19+
echo "::add-mask::${ACCESS_TOKEN}"
20+
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
21+
echo "FAIL: Could not extract JFrog access token"
22+
exit 1
23+
fi
24+
echo "JFROG_ACCESS_TOKEN=${ACCESS_TOKEN}" >> "$GITHUB_ENV"
25+
echo "JFrog OIDC token obtained successfully"
26+
27+
- name: Configure Go
28+
shell: bash
29+
run: |
30+
set -euo pipefail
31+
echo "GOPROXY=https://databricks.jfrog.io/artifactory/api/go/db-golang,direct" >> "$GITHUB_ENV"
32+
echo "GONOSUMDB=*" >> "$GITHUB_ENV"
33+
printf "machine databricks.jfrog.io\nlogin gha-service-account\npassword %s\n" "${JFROG_ACCESS_TOKEN}" > ~/.netrc
34+
chmod 600 ~/.netrc
35+
echo "Go configured to use JFrog registry"

.github/workflows/dco-check.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ permissions:
1010

1111
jobs:
1212
dco-check:
13-
runs-on: ubuntu-latest
13+
runs-on:
14+
group: databricks-protected-runner-group
15+
labels: linux-ubuntu-latest
1416
name: Check DCO Sign-off
1517
steps:
1618
- name: Checkout
17-
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
19+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1820
with:
1921
fetch-depth: 0
2022

.github/workflows/go.yml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,24 @@ on:
88

99
permissions:
1010
contents: read
11+
id-token: write
1112

1213
jobs:
1314
lint:
1415
name: Lint
15-
runs-on: ubuntu-latest
16+
runs-on:
17+
group: databricks-protected-runner-group
18+
labels: linux-ubuntu-latest
1619

1720
steps:
1821
- name: Check out code into the Go module directory
19-
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
22+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23+
24+
- name: Setup JFrog
25+
uses: ./.github/actions/setup-jfrog
2026

2127
- name: Set up Go Toolchain
22-
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
28+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
2329
with:
2430
go-version: '1.20.x'
2531
cache: false
@@ -33,21 +39,25 @@ jobs:
3339
strategy:
3440
matrix:
3541
go-version: [1.20.x]
36-
os: [ubuntu-latest]
37-
runs-on: ubuntu-latest
42+
runs-on:
43+
group: databricks-protected-runner-group
44+
labels: linux-ubuntu-latest
3845

3946
steps:
4047
- name: Check out code into the Go module directory
41-
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
48+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
49+
50+
- name: Setup JFrog
51+
uses: ./.github/actions/setup-jfrog
4252

4353
- name: Set up Go Toolchain
44-
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
54+
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
4555
with:
4656
go-version: ${{ matrix.go-version }}
4757
cache: false
4858

4959
- name: Cache Go artifacts
50-
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
60+
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
5161
with:
5262
path: |
5363
~/go/pkg/mod

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ linters-settings:
6363
exclude-generated: true
6464
severity: "low"
6565
confidence: "low"
66+
nolintlint:
67+
allow-unused: true
6668

6769
run:
6870
timeout: 5m

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,34 @@ To disable Cloud Fetch (e.g., when handling smaller datasets or to avoid additio
5656
token:[your token]@[Workspace hostname]:[Port number][Endpoint HTTP Path]?useCloudFetch=false
5757
```
5858

59+
### Telemetry Configuration (Optional)
60+
61+
The driver includes optional telemetry to help improve performance and reliability. Telemetry is **disabled by default** and requires explicit opt-in.
62+
63+
**Opt-in to telemetry** (respects server-side feature flags):
64+
```
65+
token:[your token]@[Workspace hostname]:[Port number][Endpoint HTTP Path]?enableTelemetry=true
66+
```
67+
68+
**Opt-out of telemetry** (explicitly disable):
69+
```
70+
token:[your token]@[Workspace hostname]:[Port number][Endpoint HTTP Path]?enableTelemetry=false
71+
```
72+
73+
**What data is collected:**
74+
- ✅ Query latency and performance metrics
75+
- ✅ Error codes (not error messages)
76+
- ✅ Feature usage (CloudFetch, LZ4, etc.)
77+
- ✅ Driver version and environment info
78+
79+
**What is NOT collected:**
80+
- ❌ SQL query text
81+
- ❌ Query results or data values
82+
- ❌ Table/column names
83+
- ❌ User identities or credentials
84+
85+
Telemetry has < 1% performance overhead and uses circuit breaker protection to ensure it never impacts your queries. For more details, see `telemetry/DESIGN.md` and `telemetry/TROUBLESHOOTING.md`.
86+
5987
### Connecting with a new Connector
6088

6189
You can also connect with a new connector object. For example:

auth/oauth/u2m/authenticator.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,17 @@ func NewAuthenticator(hostName string, timeout time.Duration) (auth.Authenticato
4040
cloud := oauth.InferCloudFromHost(hostName)
4141

4242
var clientID, redirectURL string
43-
if cloud == oauth.AWS {
43+
switch cloud {
44+
case oauth.AWS:
4445
clientID = awsClientId
4546
redirectURL = awsRedirectURL
46-
} else if cloud == oauth.Azure {
47+
case oauth.Azure:
4748
clientID = azureClientId
4849
redirectURL = azureRedirectURL
49-
} else if cloud == oauth.GCP {
50+
case oauth.GCP:
5051
clientID = gcpClientId
5152
redirectURL = gcpRedirectURL
52-
} else {
53+
default:
5354
return nil, errors.New("unhandled cloud type: " + cloud.String())
5455
}
5556

@@ -147,14 +148,14 @@ func (tsp *tokenSourceProvider) GetTokenSource() (oauth2.TokenSource, error) {
147148
if err != nil {
148149
return nil, err
149150
}
150-
defer listener.Close()
151+
defer listener.Close() //nolint:errcheck
151152

152153
srv := &http.Server{
153154
ReadHeaderTimeout: 3 * time.Second,
154155
WriteTimeout: 30 * time.Second,
155156
}
156157

157-
defer srv.Close()
158+
defer srv.Close() //nolint:errcheck
158159

159160
// Start local server to wait for callback
160161
go func() {
@@ -209,7 +210,7 @@ func (tsp *tokenSourceProvider) ServeHTTP(w http.ResponseWriter, r *http.Request
209210
if resp.err != "" {
210211
log.Error().Msg(resp.err)
211212
w.WriteHeader(http.StatusBadRequest)
212-
_, err := w.Write([]byte(errorHTML("Identity Provider returned an error: " + resp.err)))
213+
_, err := w.Write([]byte(errorHTML("Identity Provider returned an error: " + resp.err))) //nolint:gosec // XSS not a concern for local OAuth callback
213214
if err != nil {
214215
log.Error().Err(err).Msg("unable to write error response")
215216
}

auth/tokenprovider/exchange.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (p *FederationProvider) tryTokenExchange(ctx context.Context, subjectToken
138138
}
139139

140140
// Create request
141-
req, err := http.NewRequestWithContext(ctx, "POST", exchangeURL, strings.NewReader(data.Encode()))
141+
req, err := http.NewRequestWithContext(ctx, "POST", exchangeURL, strings.NewReader(data.Encode())) //nolint:gosec // URL is from trusted config
142142
if err != nil {
143143
return nil, fmt.Errorf("failed to create request: %w", err)
144144
}
@@ -147,11 +147,11 @@ func (p *FederationProvider) tryTokenExchange(ctx context.Context, subjectToken
147147
req.Header.Set("Accept", "*/*")
148148

149149
// Make request
150-
resp, err := p.httpClient.Do(req)
150+
resp, err := p.httpClient.Do(req) //nolint:gosec // G704: URL is from trusted configuration
151151
if err != nil {
152152
return nil, fmt.Errorf("request failed: %w", err)
153153
}
154-
defer resp.Body.Close()
154+
defer resp.Body.Close() //nolint:errcheck
155155

156156
body, err := io.ReadAll(resp.Body)
157157
if err != nil {

auth/tokenprovider/federation_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ func TestFederationProvider_TokenExchangeSuccess(t *testing.T) {
108108
assert.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"))
109109
assert.Equal(t, "*/*", r.Header.Get("Accept"))
110110

111-
// Parse form data
111+
// Parse form data - limit body size to prevent G120
112+
r.Body = http.MaxBytesReader(w, r.Body, 1<<20)
112113
err := r.ParseForm()
113114
require.NoError(t, err)
114115

@@ -155,13 +156,14 @@ func TestFederationProvider_TokenExchangeWithClientID(t *testing.T) {
155156

156157
// Create mock server that checks for client_id
157158
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
159+
r.Body = http.MaxBytesReader(w, r.Body, 1<<20)
158160
err := r.ParseForm()
159161
require.NoError(t, err)
160162

161163
// Verify client_id is present
162164
assert.Equal(t, clientID, r.FormValue("client_id"))
163165

164-
response := map[string]interface{}{
166+
response := map[string]interface{}{ //nolint:gosec // G101: test token, not a real credential
165167
"access_token": "sp-wide-federation-token",
166168
"token_type": "Bearer",
167169
"expires_in": 3600,

auth/tokenprovider/provider_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func TestExternalTokenProvider(t *testing.T) {
146146
callCount := 0
147147
tokenFunc := func() (string, error) {
148148
callCount++
149-
return "external-token-" + string(rune(callCount)), nil
149+
return "external-token-" + string(rune(callCount)), nil //nolint:gosec // G115: test counter, values are always small
150150
}
151151

152152
provider := NewExternalTokenProvider(tokenFunc)
@@ -211,7 +211,7 @@ func TestExternalTokenProvider(t *testing.T) {
211211
counter := 0
212212
tokenFunc := func() (string, error) {
213213
counter++
214-
return "token-" + string(rune(counter)), nil
214+
return "token-" + string(rune(counter)), nil //nolint:gosec // G115: test counter, values are always small
215215
}
216216

217217
provider := NewExternalTokenProvider(tokenFunc)

0 commit comments

Comments
 (0)