forked from TrueCloudLab/lego
chore: update to go1.20 (#1993)
This commit is contained in:
parent
c365d7efc8
commit
b37d60c033
13 changed files with 144 additions and 92 deletions
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
name: Build and deploy documentation
|
name: Build and deploy documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.20'
|
GO_VERSION: stable
|
||||||
HUGO_VERSION: '0.117.0'
|
HUGO_VERSION: '0.117.0'
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
|
|
28
.github/workflows/go-cross.yml
vendored
28
.github/workflows/go-cross.yml
vendored
|
@ -16,37 +16,19 @@ jobs:
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ '1.19', '1.20', 1.x ]
|
go-version: [ stable, oldstable ]
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# https://github.com/marketplace/actions/setup-go-environment
|
|
||||||
- name: Set up Go ${{ matrix.go-version }}
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.go-version }}
|
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/checkout
|
# https://github.com/marketplace/actions/checkout
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/cache
|
# https://github.com/marketplace/actions/setup-go-environment
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ matrix.go-version }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
# In order:
|
go-version: ${{ matrix.go-version }}
|
||||||
# * Module download cache
|
|
||||||
# * Build cache (Linux)
|
|
||||||
# * Build cache (Mac)
|
|
||||||
# * Build cache (Windows)
|
|
||||||
path: |
|
|
||||||
~/go/pkg/mod
|
|
||||||
~/.cache/go-build
|
|
||||||
~/Library/Caches/go-build
|
|
||||||
%LocalAppData%\go-build
|
|
||||||
key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ matrix.go-version }}-go-
|
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v -cover ./...
|
run: go test -v -cover ./...
|
||||||
|
|
21
.github/workflows/pr.yml
vendored
21
.github/workflows/pr.yml
vendored
|
@ -12,8 +12,8 @@ jobs:
|
||||||
name: Main Process
|
name: Main Process
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.20'
|
GO_VERSION: stable
|
||||||
GOLANGCI_LINT_VERSION: v1.53.1
|
GOLANGCI_LINT_VERSION: v1.54.1
|
||||||
HUGO_VERSION: '0.117.0'
|
HUGO_VERSION: '0.117.0'
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
LEGO_E2E_TESTS: CI
|
LEGO_E2E_TESTS: CI
|
||||||
|
@ -21,26 +21,17 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/setup-go-environment
|
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/checkout
|
# https://github.com/marketplace/actions/checkout
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/cache
|
# https://github.com/marketplace/actions/setup-go-environment
|
||||||
- name: Cache Go modules
|
- name: Set up Go ${{ env.GO_VERSION }}
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
path: ~/go/pkg/mod
|
go-version: ${{ env.GO_VERSION }}
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go-
|
|
||||||
|
|
||||||
- name: Check and get dependencies
|
- name: Check and get dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
name: Release version
|
name: Release version
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
GO_VERSION: '1.20'
|
GO_VERSION: stable
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -35,13 +35,14 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
|
||||||
}
|
}
|
||||||
|
|
||||||
var responses []acme.Authorization
|
var responses []acme.Authorization
|
||||||
failures := make(obtainError)
|
|
||||||
|
failures := newObtainError()
|
||||||
for i := 0; i < len(order.Authorizations); i++ {
|
for i := 0; i < len(order.Authorizations); i++ {
|
||||||
select {
|
select {
|
||||||
case res := <-resc:
|
case res := <-resc:
|
||||||
responses = append(responses, res)
|
responses = append(responses, res)
|
||||||
case err := <-errc:
|
case err := <-errc:
|
||||||
failures[err.Domain] = err.Error
|
failures.Add(err.Domain, err.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +53,7 @@ func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authoriz
|
||||||
close(resc)
|
close(resc)
|
||||||
close(errc)
|
close(errc)
|
||||||
|
|
||||||
// be careful to not return an empty failures map;
|
return responses, failures.Join()
|
||||||
// even if empty, they become non-nil error values
|
|
||||||
if len(failures) > 0 {
|
|
||||||
return responses, failures
|
|
||||||
}
|
|
||||||
return responses, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) {
|
func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder, force bool) {
|
||||||
|
|
|
@ -149,11 +149,11 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
||||||
|
|
||||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
failures := make(obtainError)
|
failures := newObtainError()
|
||||||
cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple, request.PreferredChain)
|
cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple, request.PreferredChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, auth := range authz {
|
for _, auth := range authz {
|
||||||
failures[challenge.GetTargetedDomain(auth)] = err
|
failures.Add(challenge.GetTargetedDomain(auth), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,12 +161,7 @@ func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) {
|
||||||
c.deactivateAuthorizations(order, true)
|
c.deactivateAuthorizations(order, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not return an empty failures map, because
|
return cert, failures.Join()
|
||||||
// it would still be a non-nil error value
|
|
||||||
if len(failures) > 0 {
|
|
||||||
return cert, failures
|
|
||||||
}
|
|
||||||
return cert, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
|
// ObtainForCSR tries to obtain a certificate matching the CSR passed into it.
|
||||||
|
@ -219,11 +214,11 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
||||||
|
|
||||||
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
failures := make(obtainError)
|
failures := newObtainError()
|
||||||
cert, err := c.getForCSR(domains, order, request.Bundle, request.CSR.Raw, nil, request.PreferredChain)
|
cert, err := c.getForCSR(domains, order, request.Bundle, request.CSR.Raw, nil, request.PreferredChain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, auth := range authz {
|
for _, auth := range authz {
|
||||||
failures[challenge.GetTargetedDomain(auth)] = err
|
failures.Add(challenge.GetTargetedDomain(auth), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,12 +231,7 @@ func (c *Certifier) ObtainForCSR(request ObtainForCSRRequest) (*Resource, error)
|
||||||
cert.CSR = certcrypto.PEMEncode(request.CSR)
|
cert.CSR = certcrypto.PEMEncode(request.CSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not return an empty failures map,
|
return cert, failures.Join()
|
||||||
// because it would still be a non-nil error value
|
|
||||||
if len(failures) > 0 {
|
|
||||||
return cert, failures
|
|
||||||
}
|
|
||||||
return cert, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool, preferredChain string) (*Resource, error) {
|
func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool, preferredChain string) (*Resource, error) {
|
||||||
|
|
|
@ -1,27 +1,37 @@
|
||||||
package certificate
|
package certificate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// obtainError is returned when there are specific errors available per domain.
|
type obtainError struct {
|
||||||
type obtainError map[string]error
|
data map[string]error
|
||||||
|
}
|
||||||
|
|
||||||
func (e obtainError) Error() string {
|
func newObtainError() *obtainError {
|
||||||
buffer := bytes.NewBufferString("error: one or more domains had a problem:\n")
|
return &obtainError{data: make(map[string]error)}
|
||||||
|
}
|
||||||
|
|
||||||
var domains []string
|
func (e *obtainError) Add(domain string, err error) {
|
||||||
for domain := range e {
|
e.data[domain] = err
|
||||||
domains = append(domains, domain)
|
}
|
||||||
|
|
||||||
|
func (e *obtainError) Join() error {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
sort.Strings(domains)
|
|
||||||
|
|
||||||
for _, domain := range domains {
|
if len(e.data) == 0 {
|
||||||
_, _ = fmt.Fprintf(buffer, "[%s] %s\n", domain, e[domain])
|
return nil
|
||||||
}
|
}
|
||||||
return buffer.String()
|
|
||||||
|
var err error
|
||||||
|
for d, e := range e.data {
|
||||||
|
err = errors.Join(err, fmt.Errorf("%s: %w", d, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("error: one or more domains had a problem:\n%w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type domainError struct {
|
type domainError struct {
|
||||||
|
|
70
certificate/errors_test.go
Normal file
70
certificate/errors_test.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package certificate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TomatoError struct{}
|
||||||
|
|
||||||
|
func (t TomatoError) Error() string {
|
||||||
|
return "tomato"
|
||||||
|
}
|
||||||
|
|
||||||
|
type CarrotError struct{}
|
||||||
|
|
||||||
|
func (t CarrotError) Error() string {
|
||||||
|
return "carrot"
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_obtainError_Join(t *testing.T) {
|
||||||
|
failures := newObtainError()
|
||||||
|
|
||||||
|
failures.Add("example.com", &TomatoError{})
|
||||||
|
|
||||||
|
err := failures.Join()
|
||||||
|
|
||||||
|
to := &TomatoError{}
|
||||||
|
assert.ErrorAs(t, err, &to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_obtainError_Join_multiple_domains(t *testing.T) {
|
||||||
|
failures := newObtainError()
|
||||||
|
|
||||||
|
failures.Add("example.com", &TomatoError{})
|
||||||
|
failures.Add("example.org", &CarrotError{})
|
||||||
|
|
||||||
|
err := failures.Join()
|
||||||
|
|
||||||
|
to := &TomatoError{}
|
||||||
|
assert.ErrorAs(t, err, &to)
|
||||||
|
|
||||||
|
ca := &CarrotError{}
|
||||||
|
assert.ErrorAs(t, err, &ca)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_obtainError_Join_no_error(t *testing.T) {
|
||||||
|
failures := newObtainError()
|
||||||
|
|
||||||
|
assert.NoError(t, failures.Join())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_obtainError_Join_same_domain(t *testing.T) {
|
||||||
|
failures := newObtainError()
|
||||||
|
|
||||||
|
failures.Add("example.com", &TomatoError{})
|
||||||
|
failures.Add("example.com", &CarrotError{})
|
||||||
|
|
||||||
|
err := failures.Join()
|
||||||
|
|
||||||
|
to := &TomatoError{}
|
||||||
|
if errors.As(err, &to) {
|
||||||
|
require.Fail(t, "TomatoError should be overridden by CarrotError")
|
||||||
|
}
|
||||||
|
|
||||||
|
ca := &CarrotError{}
|
||||||
|
assert.ErrorAs(t, err, &ca)
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/go-acme/lego/v4
|
module github.com/go-acme/lego/v4
|
||||||
|
|
||||||
go 1.19
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute/metadata v0.2.3
|
cloud.google.com/go/compute/metadata v0.2.3
|
||||||
|
|
|
@ -54,7 +54,7 @@ type DNSRequest struct {
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
type GetDNSSettingsAPIResponse struct {
|
type GetDNSSettingsAPIResponse struct {
|
||||||
Response GetDNSSettingsResponse `json:"Response" mapstructure:"Response"`
|
Response GetDNSSettingsResponse `json:"Response" mapstructure:"Response"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetDNSSettingsResponse struct {
|
type GetDNSSettingsResponse struct {
|
||||||
|
|
|
@ -11,26 +11,26 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
|
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultBaseURL = "https://ipv64.net"
|
const defaultBaseURL = "https://ipv64.net"
|
||||||
|
|
||||||
const authorizationHeader = "Authorization"
|
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
apiKey string
|
|
||||||
|
|
||||||
baseURL *url.URL
|
baseURL *url.URL
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(apiKey string) *Client {
|
func NewClient(hc *http.Client) *Client {
|
||||||
baseURL, _ := url.Parse(defaultBaseURL)
|
baseURL, _ := url.Parse(defaultBaseURL)
|
||||||
|
|
||||||
|
if hc == nil {
|
||||||
|
hc = &http.Client{Timeout: 15 * time.Second}
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
apiKey: apiKey,
|
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
HTTPClient: &http.Client{Timeout: 15 * time.Second},
|
HTTPClient: hc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,8 +91,6 @@ func (c Client) DeleteRecord(ctx context.Context, domain, prefix, recordType, co
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) do(req *http.Request, result any) error {
|
func (c Client) do(req *http.Request, result any) error {
|
||||||
req.Header.Set(authorizationHeader, fmt.Sprintf("Bearer %s", c.apiKey))
|
|
||||||
|
|
||||||
if req.Method != http.MethodGet {
|
if req.Method != http.MethodGet {
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
}
|
}
|
||||||
|
@ -140,3 +138,16 @@ func parseError(req *http.Request, resp *http.Response) error {
|
||||||
|
|
||||||
return errAPI
|
return errAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OAuthStaticAccessToken(client *http.Client, accessToken string) *http.Client {
|
||||||
|
if client == nil {
|
||||||
|
client = &http.Client{Timeout: 15 * time.Second}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Transport = &oauth2.Transport{
|
||||||
|
Source: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken}),
|
||||||
|
Base: client.Transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
|
@ -15,12 +15,14 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const testAPIKey = "secret"
|
||||||
|
|
||||||
func setupTest(t *testing.T, handler http.HandlerFunc) *Client {
|
func setupTest(t *testing.T, handler http.HandlerFunc) *Client {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
|
|
||||||
client := NewClient("secret")
|
client := NewClient(OAuthStaticAccessToken(server.Client(), testAPIKey))
|
||||||
client.HTTPClient = server.Client()
|
client.HTTPClient = server.Client()
|
||||||
client.baseURL, _ = url.Parse(server.URL)
|
client.baseURL, _ = url.Parse(server.URL)
|
||||||
|
|
||||||
|
@ -34,8 +36,8 @@ func testHandler(method, filename string, statusCode int) http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
auth := req.Header.Get(authorizationHeader)
|
auth := req.Header.Get("Authorization")
|
||||||
if auth != "Bearer secret" {
|
if auth != "Bearer "+testAPIKey {
|
||||||
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
http.Error(rw, fmt.Sprintf("invalid API key: %s", auth), http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
return nil, errors.New("ipv64: credentials missing")
|
return nil, errors.New("ipv64: credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := internal.NewClient(config.APIKey)
|
client := internal.NewClient(internal.OAuthStaticAccessToken(config.HTTPClient, config.APIKey))
|
||||||
|
|
||||||
if config.HTTPClient != nil {
|
if config.HTTPClient != nil {
|
||||||
client.HTTPClient = config.HTTPClient
|
client.HTTPClient = config.HTTPClient
|
||||||
|
|
Loading…
Reference in a new issue