Compare commits

...

13 Commits

Author SHA1 Message Date
Hidetake Iwata
1895099836 Fix flaky system test (#1164) 2024-11-03 18:08:46 +09:00
Hidetake Iwata
0e9a39a571 Infer apiVersion from KUBERNETES_EXEC_INFO environment variable (#1162)
* Infer apiVersion from KUBERNETES_EXEC_INFO

* Test client.authentication.k8s.io/v1

* Set --exec-interactive-mode

* Set --exec-interactive-mode=Never

* Fix comments
2024-11-03 17:21:25 +09:00
Hidetake Iwata
f1f2a37adc Include essential options to token cache key (#1161) 2024-10-26 21:42:23 +09:00
Hidetake Iwata
438068e9de refactor: Move useAccessToken to oidc.Provider (#1160)
* refactor: Move useAccessToken to oidc.Provider

* Generated by GitHub Actions (go / generate)

https://github.com/int128/kubelogin/actions/runs/11530911738

---------

Co-authored-by: update-generated-files-action <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-26 21:07:44 +09:00
renovate[bot]
aaf9a6a58f chore(deps): update dependency int128/go-renovate-config to v1.6.1 (#1149)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-26 18:26:49 +09:00
renovate[bot]
9567ab157b fix(deps): update kubernetes packages to v0.31.2 (#1159)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 18:08:57 +00:00
renovate[bot]
f49d73087a fix(deps): update module github.com/chromedp/chromedp to v0.11.1 (#1158)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 05:08:53 +00:00
renovate[bot]
1c84d270a9 fix(deps): update module github.com/chromedp/chromedp to v0.11.0 (#1157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 21:45:14 +00:00
renovate[bot]
1b245f9947 fix(deps): update module github.com/vektra/mockery/v2 to v2.46.3 (#1156)
* fix(deps): update module github.com/vektra/mockery/v2 to v2.46.3

* Generated by GitHub Actions (go / generate)

https://github.com/int128/kubelogin/actions/runs/11320535114

* Empty commit to trigger GitHub Actions

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: update-generated-files-action <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: int128-renovate-merge-bot[bot] <132176788+int128-renovate-merge-bot[bot]@users.noreply.github.com>
2024-10-14 06:12:32 +00:00
renovate[bot]
963942afad fix(deps): update module golang.org/x/term to v0.25.0 (#1155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-04 18:10:39 +00:00
renovate[bot]
24357b6ea7 chore(deps): update dependency go to v1.23.2 (#1153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 20:09:23 +00:00
renovate[bot]
4e7a44cdbe fix(deps): update module github.com/vektra/mockery/v2 to v2.46.2 (#1154)
* fix(deps): update module github.com/vektra/mockery/v2 to v2.46.2

* Generated by GitHub Actions (go / generate)

https://github.com/int128/kubelogin/actions/runs/11147159678

* Empty commit to trigger GitHub Actions

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: update-generated-files-action <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: int128-renovate-merge-bot[bot] <132176788+int128-renovate-merge-bot[bot]@users.noreply.github.com>
2024-10-02 18:11:24 +00:00
renovate[bot]
3f7513754c fix(deps): update module github.com/vektra/mockery/v2 to v2.46.1 (#1152)
* fix(deps): update module github.com/vektra/mockery/v2 to v2.46.1

* Generated by GitHub Actions (go / generate)

https://github.com/int128/kubelogin/actions/runs/11081139611

* Empty commit to trigger GitHub Actions

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: update-generated-files-action <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: int128-renovate-merge-bot[bot] <132176788+int128-renovate-merge-bot[bot]@users.noreply.github.com>
2024-09-28 06:10:30 +00:00
47 changed files with 480 additions and 219 deletions

View File

@@ -2,10 +2,10 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>int128/renovate-base",
"github>int128/go-renovate-config#v1.6.0",
"github>int128/go-renovate-config:go-directive#v1.6.0",
"github>int128/go-renovate-config:github-actions#v1.6.0",
"github>int128/go-renovate-config:kubernetes#v1.6.0",
"github>int128/go-renovate-config:kustomization-github-releases#v1.6.0",
"github>int128/go-renovate-config#v1.6.1",
"github>int128/go-renovate-config:go-directive#v1.6.1",
"github>int128/go-renovate-config:github-actions#v1.6.1",
"github>int128/go-renovate-config:kubernetes#v1.6.1",
"github>int128/go-renovate-config:kustomization-github-releases#v1.6.1",
],
}

16
go.mod
View File

@@ -1,9 +1,9 @@
module github.com/int128/kubelogin
go 1.23.1
go 1.23.2
require (
github.com/chromedp/chromedp v0.10.0
github.com/chromedp/chromedp v0.11.1
github.com/coreos/go-oidc/v3 v3.11.0
github.com/gofrs/flock v0.12.1
github.com/golang-jwt/jwt/v5 v5.2.1
@@ -17,16 +17,16 @@ require (
github.com/stretchr/testify v1.9.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/term v0.24.0
golang.org/x/term v0.25.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
k8s.io/apimachinery v0.31.2
k8s.io/client-go v0.31.2
k8s.io/klog/v2 v2.130.1
)
require (
github.com/chromedp/cdproto v0.0.0-20240801214329-3f85d328b335 // indirect
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
@@ -49,7 +49,7 @@ require (
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.3.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect

33
go.sum
View File

@@ -34,12 +34,12 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chromedp/cdproto v0.0.0-20240801214329-3f85d328b335 h1:bATMoZLH2QGct1kzDxfmeBUQI/QhQvB0mBrOTct+YlQ=
github.com/chromedp/cdproto v0.0.0-20240801214329-3f85d328b335/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.10.0 h1:bRclRYVpMm/UVD76+1HcRW9eV3l58rFfy7AdBvKab1E=
github.com/chromedp/chromedp v0.10.0/go.mod h1:ei/1ncZIqXX1YnAYDkxhD4gzBgavMEUu7JCKvztdomE=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb h1:noKVm2SsG4v0Yd0lHNtFYc9EUxIVvrr4kJ6hM8wvIYU=
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb/go.mod h1:4XqMl3iIW08jtieURWL6Tt5924w21pxirC6th662XUM=
github.com/chromedp/chromedp v0.11.1 h1:Spca8egFqUlv+JDW+yIs+ijlHlJDPufgrfXPwtq6NMs=
github.com/chromedp/chromedp v0.11.1/go.mod h1:lr8dFRLKsdTTWb75C/Ttol2vnBKOSnt0BW8R9Xaupi8=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
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=
@@ -360,17 +360,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
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=
@@ -533,12 +532,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
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=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package handler_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package oidcserver_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package cmd_mock

View File

@@ -0,0 +1,90 @@
// Code generated by mockery v2.46.3. DO NOT EDIT.
package reader_mock
import (
credentialplugin "github.com/int128/kubelogin/pkg/credentialplugin"
mock "github.com/stretchr/testify/mock"
)
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Read provides a mock function with given fields:
func (_m *MockInterface) Read() (credentialplugin.Input, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for Read")
}
var r0 credentialplugin.Input
var r1 error
if rf, ok := ret.Get(0).(func() (credentialplugin.Input, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() credentialplugin.Input); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(credentialplugin.Input)
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read'
type MockInterface_Read_Call struct {
*mock.Call
}
// Read is a helper method to define mock.On call
func (_e *MockInterface_Expecter) Read() *MockInterface_Read_Call {
return &MockInterface_Read_Call{Call: _e.mock.On("Read")}
}
func (_c *MockInterface_Read_Call) Run(run func()) *MockInterface_Read_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockInterface_Read_Call) Return(_a0 credentialplugin.Input, _a1 error) *MockInterface_Read_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockInterface_Read_Call) RunAndReturn(run func() (credentialplugin.Input, error)) *MockInterface_Read_Call {
_c.Call.Return(run)
return _c
}
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package writer_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package browser_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package clock_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package logger_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package logger_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package logger_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package reader_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package jwt_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package loader_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package writer_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package client_mock
@@ -27,9 +27,9 @@ func (_m *MockFactoryInterface) EXPECT() *MockFactoryInterface_Expecter {
return &MockFactoryInterface_Expecter{mock: &_m.Mock}
}
// New provides a mock function with given fields: ctx, p, tlsClientConfig, useAccessToken
func (_m *MockFactoryInterface) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config, useAccessToken bool) (client.Interface, error) {
ret := _m.Called(ctx, p, tlsClientConfig, useAccessToken)
// New provides a mock function with given fields: ctx, p, tlsClientConfig
func (_m *MockFactoryInterface) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config) (client.Interface, error) {
ret := _m.Called(ctx, p, tlsClientConfig)
if len(ret) == 0 {
panic("no return value specified for New")
@@ -37,19 +37,19 @@ func (_m *MockFactoryInterface) New(ctx context.Context, p oidc.Provider, tlsCli
var r0 client.Interface
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config, bool) (client.Interface, error)); ok {
return rf(ctx, p, tlsClientConfig, useAccessToken)
if rf, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config) (client.Interface, error)); ok {
return rf(ctx, p, tlsClientConfig)
}
if rf, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config, bool) client.Interface); ok {
r0 = rf(ctx, p, tlsClientConfig, useAccessToken)
if rf, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config) client.Interface); ok {
r0 = rf(ctx, p, tlsClientConfig)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(client.Interface)
}
}
if rf, ok := ret.Get(1).(func(context.Context, oidc.Provider, tlsclientconfig.Config, bool) error); ok {
r1 = rf(ctx, p, tlsClientConfig, useAccessToken)
if rf, ok := ret.Get(1).(func(context.Context, oidc.Provider, tlsclientconfig.Config) error); ok {
r1 = rf(ctx, p, tlsClientConfig)
} else {
r1 = ret.Error(1)
}
@@ -66,14 +66,13 @@ type MockFactoryInterface_New_Call struct {
// - ctx context.Context
// - p oidc.Provider
// - tlsClientConfig tlsclientconfig.Config
// - useAccessToken bool
func (_e *MockFactoryInterface_Expecter) New(ctx interface{}, p interface{}, tlsClientConfig interface{}, useAccessToken interface{}) *MockFactoryInterface_New_Call {
return &MockFactoryInterface_New_Call{Call: _e.mock.On("New", ctx, p, tlsClientConfig, useAccessToken)}
func (_e *MockFactoryInterface_Expecter) New(ctx interface{}, p interface{}, tlsClientConfig interface{}) *MockFactoryInterface_New_Call {
return &MockFactoryInterface_New_Call{Call: _e.mock.On("New", ctx, p, tlsClientConfig)}
}
func (_c *MockFactoryInterface_New_Call) Run(run func(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config, useAccessToken bool)) *MockFactoryInterface_New_Call {
func (_c *MockFactoryInterface_New_Call) Run(run func(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config)) *MockFactoryInterface_New_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(oidc.Provider), args[2].(tlsclientconfig.Config), args[3].(bool))
run(args[0].(context.Context), args[1].(oidc.Provider), args[2].(tlsclientconfig.Config))
})
return _c
}
@@ -83,7 +82,7 @@ func (_c *MockFactoryInterface_New_Call) Return(_a0 client.Interface, _a1 error)
return _c
}
func (_c *MockFactoryInterface_New_Call) RunAndReturn(run func(context.Context, oidc.Provider, tlsclientconfig.Config, bool) (client.Interface, error)) *MockFactoryInterface_New_Call {
func (_c *MockFactoryInterface_New_Call) RunAndReturn(run func(context.Context, oidc.Provider, tlsclientconfig.Config) (client.Interface, error)) *MockFactoryInterface_New_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package client_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package logger_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package loader_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package repository_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package authentication_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package credentialplugin_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package setup_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package standalone_mock

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.46.0. DO NOT EDIT.
// Code generated by mockery v2.46.3. DO NOT EDIT.
package io_mock

View File

@@ -125,7 +125,6 @@ func TestCmd_Run(t *testing.T) {
RedirectURLHostname: "localhost",
},
},
UseAccessToken: false,
},
},
"FullOptions": {
@@ -153,7 +152,6 @@ func TestCmd_Run(t *testing.T) {
RedirectURLHostname: "localhost",
},
},
UseAccessToken: false,
},
},
"AccessToken": {
@@ -166,8 +164,9 @@ func TestCmd_Run(t *testing.T) {
in: credentialplugin.Input{
TokenCacheDir: filepath.Join(userHomeDir, ".kube/cache/oidc-login"),
Provider: oidc.Provider{
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
UseAccessToken: true,
},
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
@@ -176,7 +175,6 @@ func TestCmd_Run(t *testing.T) {
RedirectURLHostname: "localhost",
},
},
UseAccessToken: true,
},
},
"HomedirExpansion": {
@@ -207,7 +205,6 @@ func TestCmd_Run(t *testing.T) {
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{filepath.Join(userHomeDir, ".kube/ca.crt")},
},
UseAccessToken: false,
},
},
}

View File

@@ -77,17 +77,17 @@ func (cmd *GetToken) New() *cobra.Command {
}
in := credentialplugin.Input{
Provider: oidc.Provider{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
UsePKCE: o.UsePKCE,
ExtraScopes: o.ExtraScopes,
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
UsePKCE: o.UsePKCE,
UseAccessToken: o.UseAccessToken,
ExtraScopes: o.ExtraScopes,
},
TokenCacheDir: o.TokenCacheDir,
GrantOptionSet: grantOptionSet,
TLSClientConfig: o.tlsOptions.tlsClientConfig(),
ForceRefresh: o.ForceRefresh,
UseAccessToken: o.UseAccessToken,
}
if err := cmd.GetToken.Do(c.Context(), in); err != nil {
return fmt.Errorf("get-token: %w", err)

View File

@@ -0,0 +1,39 @@
// Package reader provides a loader for the credential plugin.
package reader
import (
"encoding/json"
"fmt"
"os"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/credentialplugin"
"k8s.io/client-go/pkg/apis/clientauthentication"
)
var Set = wire.NewSet(
wire.Struct(new(Reader), "*"),
wire.Bind(new(Interface), new(*Reader)),
)
type Interface interface {
Read() (credentialplugin.Input, error)
}
type Reader struct{}
// Read parses the environment variable KUBERNETES_EXEC_INFO.
// If the environment variable is not given by kubectl, Read returns a zero value.
func (r Reader) Read() (credentialplugin.Input, error) {
execInfo := os.Getenv("KUBERNETES_EXEC_INFO")
if execInfo == "" {
return credentialplugin.Input{}, nil
}
var execCredential clientauthentication.ExecCredential
if err := json.Unmarshal([]byte(execInfo), &execCredential); err != nil {
return credentialplugin.Input{}, fmt.Errorf("invalid KUBERNETES_EXEC_INFO: %w", err)
}
return credentialplugin.Input{
ClientAuthenticationAPIVersion: execCredential.APIVersion,
}, nil
}

View File

@@ -0,0 +1,44 @@
package reader
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/credentialplugin"
)
func TestReader_Read(t *testing.T) {
var reader Reader
t.Run("KUBERNETES_EXEC_INFO is empty", func(t *testing.T) {
input, err := reader.Read()
if err != nil {
t.Errorf("Read returned error: %v", err)
}
want := credentialplugin.Input{}
if diff := cmp.Diff(want, input); diff != "" {
t.Errorf("input mismatch (-want +got):\n%s", diff)
}
})
t.Run("KUBERNETES_EXEC_INFO is invalid JSON", func(t *testing.T) {
t.Setenv("KUBERNETES_EXEC_INFO", "invalid")
_, err := reader.Read()
if err == nil {
t.Errorf("Read wants error but no error")
}
})
t.Run("KUBERNETES_EXEC_INFO is v1", func(t *testing.T) {
t.Setenv(
"KUBERNETES_EXEC_INFO",
`{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1","spec":{"interactive":true}}`,
)
input, err := reader.Read()
if err != nil {
t.Errorf("Read returned error: %v", err)
}
want := credentialplugin.Input{ClientAuthenticationAPIVersion: "client.authentication.k8s.io/v1"}
if diff := cmp.Diff(want, input); diff != "" {
t.Errorf("input mismatch (-want +got):\n%s", diff)
}
})
}

View File

@@ -3,8 +3,15 @@ package credentialplugin
import "time"
// Input represents an input object of the credential plugin.
// This may be a zero value if the input is not available.
type Input struct {
ClientAuthenticationAPIVersion string
}
// Output represents an output object of the credential plugin.
type Output struct {
Token string
Expiry time.Time
Token string
Expiry time.Time
ClientAuthenticationAPIVersion string
}

View File

@@ -1,4 +1,4 @@
// Package writer provides a writer for a credential plugin.
// Package writer provides a writer for the credential plugin.
package writer
import (
@@ -9,6 +9,7 @@ import (
"github.com/int128/kubelogin/pkg/credentialplugin"
"github.com/int128/kubelogin/pkg/infrastructure/stdio"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthenticationv1 "k8s.io/client-go/pkg/apis/clientauthentication/v1"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
)
@@ -27,19 +28,44 @@ type Writer struct {
// Write writes the ExecCredential to standard output for kubectl.
func (w *Writer) Write(out credentialplugin.Output) error {
ec := &clientauthenticationv1beta1.ExecCredential{
TypeMeta: metav1.TypeMeta{
APIVersion: "client.authentication.k8s.io/v1beta1",
Kind: "ExecCredential",
},
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
Token: out.Token,
ExpirationTimestamp: &metav1.Time{Time: out.Expiry},
},
execCredential, err := generateExecCredential(out)
if err != nil {
return fmt.Errorf("generate ExecCredential: %w", err)
}
e := json.NewEncoder(w.Stdout)
if err := e.Encode(ec); err != nil {
return fmt.Errorf("could not write the ExecCredential: %w", err)
if err := json.NewEncoder(w.Stdout).Encode(execCredential); err != nil {
return fmt.Errorf("write ExecCredential: %w", err)
}
return nil
}
func generateExecCredential(out credentialplugin.Output) (any, error) {
switch out.ClientAuthenticationAPIVersion {
// If the API version is not available, fall back to v1beta1.
case clientauthenticationv1beta1.SchemeGroupVersion.String(), "":
return &clientauthenticationv1beta1.ExecCredential{
TypeMeta: metav1.TypeMeta{
APIVersion: clientauthenticationv1beta1.SchemeGroupVersion.String(),
Kind: "ExecCredential",
},
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
Token: out.Token,
ExpirationTimestamp: &metav1.Time{Time: out.Expiry},
},
}, nil
case clientauthenticationv1.SchemeGroupVersion.String():
return &clientauthenticationv1.ExecCredential{
TypeMeta: metav1.TypeMeta{
APIVersion: clientauthenticationv1.SchemeGroupVersion.String(),
Kind: "ExecCredential",
},
Status: &clientauthenticationv1.ExecCredentialStatus{
Token: out.Token,
ExpirationTimestamp: &metav1.Time{Time: out.Expiry},
},
}, nil
default:
return nil, fmt.Errorf("unknown apiVersion: %s", out.ClientAuthenticationAPIVersion)
}
}

View File

@@ -7,7 +7,8 @@ package di
import (
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/cmd"
"github.com/int128/kubelogin/pkg/credentialplugin/writer"
credentialpluginreader "github.com/int128/kubelogin/pkg/credentialplugin/reader"
credentialpluginwriter "github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
@@ -55,7 +56,8 @@ func NewCmdForHeadless(clock.Interface, stdio.Stdin, stdio.Stdout, logger.Interf
repository.Set,
client.Set,
loader.Set,
writer.Set,
credentialpluginreader.Set,
credentialpluginwriter.Set,
)
return nil
}

View File

@@ -8,6 +8,7 @@ package di
import (
"github.com/int128/kubelogin/pkg/cmd"
reader2 "github.com/int128/kubelogin/pkg/credentialplugin/reader"
writer2 "github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
@@ -96,15 +97,17 @@ func NewCmdForHeadless(clockInterface clock.Interface, stdin stdio.Stdin, stdout
Logger: loggerInterface,
}
repositoryRepository := &repository.Repository{}
reader3 := &reader2.Reader{}
writer3 := &writer2.Writer{
Stdout: stdout,
}
getToken := &credentialplugin.GetToken{
Authentication: authenticationAuthentication,
TokenCacheRepository: repositoryRepository,
Writer: writer3,
Logger: loggerInterface,
Clock: clockInterface,
Authentication: authenticationAuthentication,
TokenCacheRepository: repositoryRepository,
CredentialPluginReader: reader3,
CredentialPluginWriter: writer3,
Logger: loggerInterface,
Clock: clockInterface,
}
cmdGetToken := &cmd.GetToken{
GetToken: getToken,

View File

@@ -24,7 +24,7 @@ var Set = wire.NewSet(
)
type FactoryInterface interface {
New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config, useAccessToken bool) (Interface, error)
New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config) (Interface, error)
}
type Factory struct {
@@ -34,7 +34,7 @@ type Factory struct {
}
// New returns an instance of infrastructure.Interface with the given configuration.
func (f *Factory) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config, useAccessToken bool) (Interface, error) {
func (f *Factory) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config) (Interface, error) {
rawTLSClientConfig, err := f.Loader.Load(tlsClientConfig)
if err != nil {
return nil, fmt.Errorf("could not load the TLS client config: %w", err)
@@ -80,7 +80,7 @@ func (f *Factory) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsc
logger: f.Logger,
supportedPKCEMethods: supportedPKCEMethods,
deviceAuthorizationEndpoint: deviceAuthorizationEndpoint,
useAccessToken: useAccessToken,
useAccessToken: p.UseAccessToken,
}, nil
}

View File

@@ -11,11 +11,12 @@ import (
// Provider represents an OIDC provider.
type Provider struct {
IssuerURL string
ClientID string
ClientSecret string // optional
ExtraScopes []string // optional
UsePKCE bool // optional
IssuerURL string
ClientID string
ClientSecret string // optional
ExtraScopes []string // optional
UsePKCE bool // optional
UseAccessToken bool // optional
}
// TokenSet represents a set of ID token and refresh token.

View File

@@ -7,6 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/tokencache"
)
@@ -16,12 +17,15 @@ func TestRepository_FindByKey(t *testing.T) {
t.Run("Success", func(t *testing.T) {
dir := t.TempDir()
key := tokencache.Key{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"openid", "email"},
CACertFilename: "/path/to/cert",
SkipTLSVerify: false,
Provider: oidc.Provider{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"openid", "email"},
},
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{"/path/to/cert"},
},
}
json := `{"id_token":"YOUR_ID_TOKEN","refresh_token":"YOUR_REFRESH_TOKEN"}`
filename, err := computeFilename(key)
@@ -50,12 +54,15 @@ func TestRepository_Save(t *testing.T) {
t.Run("Success", func(t *testing.T) {
dir := t.TempDir()
key := tokencache.Key{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"openid", "email"},
CACertFilename: "/path/to/cert",
SkipTLSVerify: false,
Provider: oidc.Provider{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"openid", "email"},
},
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{"/path/to/cert"},
},
}
tokenSet := oidc.TokenSet{IDToken: "YOUR_ID_TOKEN", RefreshToken: "YOUR_REFRESH_TOKEN"}
if err := r.Save(dir, key, tokenSet); err != nil {

View File

@@ -1,13 +1,13 @@
package tokencache
import (
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
)
// Key represents a key of a token cache.
type Key struct {
IssuerURL string
ClientID string
ClientSecret string
Username string
ExtraScopes []string
CACertFilename string
CACertData string
SkipTLSVerify bool
Provider oidc.Provider
TLSClientConfig tlsclientconfig.Config
Username string
}

View File

@@ -73,7 +73,7 @@ type Authentication struct {
func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
u.Logger.V(1).Infof("initializing an OpenID Connect client")
oidcClient, err := u.ClientFactory.New(ctx, in.Provider, in.TLSClientConfig, in.UseAccessToken)
oidcClient, err := u.ClientFactory.New(ctx, in.Provider, in.TLSClientConfig)
if err != nil {
return nil, fmt.Errorf("oidc error: %w", err)
}

View File

@@ -56,7 +56,7 @@ func TestAuthentication_Do(t *testing.T) {
}, nil)
mockClientFactory := client_mock.NewMockFactoryInterface(t)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig, false).
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockClient, nil)
u := Authentication{
ClientFactory: mockClientFactory,
@@ -113,7 +113,7 @@ func TestAuthentication_Do(t *testing.T) {
}, nil)
mockClientFactory := client_mock.NewMockFactoryInterface(t)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig, false).
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockClient, nil)
u := Authentication{
ClientFactory: mockClientFactory,
@@ -159,7 +159,7 @@ func TestAuthentication_Do(t *testing.T) {
}, nil)
mockClientFactory := client_mock.NewMockFactoryInterface(t)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig, false).
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockClient, nil)
u := Authentication{
ClientFactory: mockClientFactory,

View File

@@ -6,11 +6,11 @@ package credentialplugin
import (
"context"
"fmt"
"strings"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/credentialplugin"
"github.com/int128/kubelogin/pkg/credentialplugin/writer"
credentialpluginreader "github.com/int128/kubelogin/pkg/credentialplugin/reader"
credentialpluginwriter "github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
@@ -36,29 +36,30 @@ type Input struct {
GrantOptionSet authentication.GrantOptionSet
TLSClientConfig tlsclientconfig.Config
ForceRefresh bool
UseAccessToken bool
}
type GetToken struct {
Authentication authentication.Interface
TokenCacheRepository repository.Interface
Writer writer.Interface
Logger logger.Interface
Clock clock.Interface
Authentication authentication.Interface
TokenCacheRepository repository.Interface
CredentialPluginReader credentialpluginreader.Interface
CredentialPluginWriter credentialpluginwriter.Interface
Logger logger.Interface
Clock clock.Interface
}
func (u *GetToken) Do(ctx context.Context, in Input) error {
u.Logger.V(1).Infof("WARNING: log may contain your secrets such as token or password")
credentialPluginInput, err := u.CredentialPluginReader.Read()
if err != nil {
return fmt.Errorf("could not read the input of credential plugin: %w", err)
}
u.Logger.V(1).Infof("credential plugin is called with apiVersion: %s", credentialPluginInput.ClientAuthenticationAPIVersion)
u.Logger.V(1).Infof("finding a token from cache directory %s", in.TokenCacheDir)
tokenCacheKey := tokencache.Key{
IssuerURL: in.Provider.IssuerURL,
ClientID: in.Provider.ClientID,
ClientSecret: in.Provider.ClientSecret,
ExtraScopes: in.Provider.ExtraScopes,
CACertFilename: strings.Join(in.TLSClientConfig.CACertFilename, ","),
CACertData: strings.Join(in.TLSClientConfig.CACertData, ","),
SkipTLSVerify: in.TLSClientConfig.SkipTLSVerify,
Provider: in.Provider,
TLSClientConfig: in.TLSClientConfig,
}
if in.GrantOptionSet.ROPCOption != nil {
tokenCacheKey.Username = in.GrantOptionSet.ROPCOption.Username
@@ -95,10 +96,11 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
if !claims.IsExpired(u.Clock) {
u.Logger.V(1).Infof("you already have a valid token until %s", claims.Expiry)
out := credentialplugin.Output{
Token: cachedTokenSet.IDToken,
Expiry: claims.Expiry,
Token: cachedTokenSet.IDToken,
Expiry: claims.Expiry,
ClientAuthenticationAPIVersion: credentialPluginInput.ClientAuthenticationAPIVersion,
}
if err := u.Writer.Write(out); err != nil {
if err := u.CredentialPluginWriter.Write(out); err != nil {
return fmt.Errorf("could not write the token to client-go: %w", err)
}
return nil
@@ -113,7 +115,6 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
CachedTokenSet: cachedTokenSet,
TLSClientConfig: in.TLSClientConfig,
ForceRefresh: in.ForceRefresh,
UseAccessToken: in.UseAccessToken,
}
authenticationOutput, err := u.Authentication.Do(ctx, authenticationInput)
if err != nil {
@@ -130,10 +131,11 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
}
u.Logger.V(1).Infof("writing the token to client-go")
out := credentialplugin.Output{
Token: authenticationOutput.TokenSet.IDToken,
Expiry: idTokenClaims.Expiry,
Token: authenticationOutput.TokenSet.IDToken,
Expiry: idTokenClaims.Expiry,
ClientAuthenticationAPIVersion: credentialPluginInput.ClientAuthenticationAPIVersion,
}
if err := u.Writer.Write(out); err != nil {
if err := u.CredentialPluginWriter.Write(out); err != nil {
return fmt.Errorf("could not write the token to client-go: %w", err)
}
return nil

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/int128/kubelogin/mocks/github.com/int128/kubelogin/pkg/credentialplugin/reader_mock"
"github.com/int128/kubelogin/mocks/github.com/int128/kubelogin/pkg/credentialplugin/writer_mock"
"github.com/int128/kubelogin/mocks/github.com/int128/kubelogin/pkg/tokencache/repository_mock"
"github.com/int128/kubelogin/mocks/github.com/int128/kubelogin/pkg/usecases/authentication_mock"
@@ -40,8 +41,12 @@ func TestGetToken_Do(t *testing.T) {
RefreshToken: "YOUR_REFRESH_TOKEN",
}
issuedOutput := credentialplugin.Output{
Token: issuedIDToken,
Expiry: expiryTime,
Token: issuedIDToken,
Expiry: expiryTime,
ClientAuthenticationAPIVersion: "client.authentication.k8s.io/v1",
}
credentialpluginInput := credentialplugin.Input{
ClientAuthenticationAPIVersion: "client.authentication.k8s.io/v1",
}
grantOptionSet := authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
@@ -51,9 +56,11 @@ func TestGetToken_Do(t *testing.T) {
t.Run("NoTokenCache", func(t *testing.T) {
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
}
ctx := context.TODO()
in := Input{
@@ -82,16 +89,21 @@ func TestGetToken_Do(t *testing.T) {
mockRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, issuedTokenSet).
Return(nil)
mockReader := reader_mock.NewMockInterface(t)
mockReader.EXPECT().
Read().
Return(credentialpluginInput, nil)
mockWriter := writer_mock.NewMockInterface(t)
mockWriter.EXPECT().
Write(issuedOutput).
Return(nil)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
CredentialPluginReader: mockReader,
CredentialPluginWriter: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
@@ -103,10 +115,12 @@ func TestGetToken_Do(t *testing.T) {
ROPCOption: &ropc.Option{Username: "YOUR_USERNAME"},
}
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Username: "YOUR_USERNAME",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
Username: "YOUR_USERNAME",
}
ctx := context.TODO()
@@ -136,16 +150,21 @@ func TestGetToken_Do(t *testing.T) {
mockRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, issuedTokenSet).
Return(nil)
mockReader := reader_mock.NewMockInterface(t)
mockReader.EXPECT().
Read().
Return(credentialplugin.Input{ClientAuthenticationAPIVersion: "client.authentication.k8s.io/v1"}, nil)
mockWriter := writer_mock.NewMockInterface(t)
mockWriter.EXPECT().
Write(issuedOutput).
Return(nil)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
CredentialPluginReader: mockReader,
CredentialPluginWriter: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
@@ -154,9 +173,11 @@ func TestGetToken_Do(t *testing.T) {
t.Run("HasValidIDToken", func(t *testing.T) {
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
}
ctx := context.TODO()
@@ -175,21 +196,28 @@ func TestGetToken_Do(t *testing.T) {
Return(mockCloser, nil)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
}).
Return(&issuedTokenSet, nil)
mockReader := reader_mock.NewMockInterface(t)
mockReader.EXPECT().
Read().
Return(credentialpluginInput, nil)
mockWriter := writer_mock.NewMockInterface(t)
mockWriter.EXPECT().
Write(issuedOutput).
Return(nil)
u := GetToken{
Authentication: authentication_mock.NewMockInterface(t),
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
Authentication: authentication_mock.NewMockInterface(t),
TokenCacheRepository: mockRepository,
CredentialPluginReader: mockReader,
CredentialPluginWriter: mockWriter,
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
@@ -198,9 +226,11 @@ func TestGetToken_Do(t *testing.T) {
t.Run("AuthenticationError", func(t *testing.T) {
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
}
ctx := context.TODO()
in := Input{
@@ -225,17 +255,24 @@ func TestGetToken_Do(t *testing.T) {
Return(mockCloser, nil)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
}).
Return(nil, errors.New("file not found"))
mockReader := reader_mock.NewMockInterface(t)
mockReader.EXPECT().
Read().
Return(credentialpluginInput, nil)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
Writer: writer_mock.NewMockInterface(t),
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
CredentialPluginReader: mockReader,
CredentialPluginWriter: writer_mock.NewMockInterface(t),
Logger: logger.New(t),
Clock: clock.Fake(expiryTime.Add(-time.Hour)),
}
if err := u.Do(ctx, in); err == nil {
t.Errorf("err wants non-nil but nil")

View File

@@ -19,7 +19,8 @@ test: build
--browser-command=$(BIN_DIR)/chromelogin
# set up the kubeconfig
kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
--exec-api-version=client.authentication.k8s.io/v1 \
--exec-interactive-mode=Never \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \

View File

@@ -28,20 +28,29 @@ func main() {
func runBrowser(ctx context.Context, url string) error {
execOpts := chromedp.DefaultExecAllocatorOptions[:]
execOpts = append(execOpts, chromedp.NoSandbox)
ctx, cancel := chromedp.NewExecAllocator(ctx, execOpts...)
defer cancel()
ctx, cancel = chromedp.NewContext(ctx, chromedp.WithLogf(log.Printf))
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := logInToDex(ctx, url); err != nil {
execOpts = append(execOpts,
chromedp.NoSandbox,
chromedp.WSURLReadTimeout(30*time.Second),
)
ctx, cancelExec := chromedp.NewExecAllocator(ctx, execOpts...)
defer cancelExec()
ctx, cancelCtx := chromedp.NewContext(ctx, chromedp.WithLogf(log.Printf))
defer cancelCtx()
log.Printf("Opening a new browser and navigating to %s", url)
if err := openBrowser(ctx, url); err != nil {
return fmt.Errorf("could not open a new browser: %w", err)
}
ctx, cancelTimeout := context.WithTimeout(ctx, 30*time.Second)
defer cancelTimeout()
log.Printf("Logging in to Dex")
if err := logInToDex(ctx); err != nil {
return fmt.Errorf("could not run the browser: %w", err)
}
return nil
}
func logInToDex(ctx context.Context, url string) error {
func openBrowser(ctx context.Context, url string) error {
for {
var location string
err := chromedp.Run(ctx,
@@ -51,14 +60,16 @@ func logInToDex(ctx context.Context, url string) error {
if err != nil {
return err
}
log.Printf("location: %s", location)
log.Printf("Location: %s", location)
if strings.HasPrefix(location, `http://`) || strings.HasPrefix(location, `https://`) {
break
return nil
}
time.Sleep(1 * time.Second)
}
}
err := chromedp.Run(ctx,
func logInToDex(ctx context.Context) error {
return chromedp.Run(ctx,
// https://dex-server:10443/dex/auth/local
chromedp.WaitVisible(`#login`),
logPageMetadata(),
@@ -73,10 +84,6 @@ func logInToDex(ctx context.Context, url string) error {
chromedp.WaitReady(`body`),
logPageMetadata(),
)
if err != nil {
return err
}
return nil
}
func logPageMetadata() chromedp.Action {
@@ -86,7 +93,7 @@ func logPageMetadata() chromedp.Action {
chromedp.Location(&location),
chromedp.Title(&title),
chromedp.ActionFunc(func(ctx context.Context) error {
log.Printf("location: %s [%s]", location, title)
log.Printf("Location: %s, Title: %s", location, title)
return nil
}),
}

View File

@@ -1,10 +1,10 @@
module github.com/int128/kubelogin/tools
go 1.23.1
go 1.23.2
require (
github.com/google/wire v0.6.0
github.com/vektra/mockery/v2 v2.46.0
github.com/vektra/mockery/v2 v2.46.3
)
require (

View File

@@ -209,8 +209,8 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/vektra/mockery/v2 v2.46.0 h1:DKIFj6hAPGwmOYiWfWzdsQtBgU8ozPXo3Bwbmf+Ku80=
github.com/vektra/mockery/v2 v2.46.0/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8=
github.com/vektra/mockery/v2 v2.46.3 h1:Aia4iShZeNTy6CZJcvweK4X97XOzXcqb+k9jm8IqBS0=
github.com/vektra/mockery/v2 v2.46.3/go.mod h1:dDivqi0ShM8A29mLgZn13yZ14MdXlTM4V360u8JDWCQ=
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=