Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ permissions:

jobs:
build:
name: Build Go ${{ matrix.go-version }}
runs-on: ubuntu-latest
name: Build Go ${{ matrix.go-version }} ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
go-version: [1.25.x]
os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-24.04-arm]
go-version: [1.25.x, 1.26.x]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
Expand Down
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ all: test

getdeps:
@mkdir -p ${GOPATH}/bin
@echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin
@echo "Installing golangci-lint" && go install tool

lint: getdeps
@echo "Running $@ check"
@${GOPATH}/bin/golangci-lint cache clean
@${GOPATH}/bin/golangci-lint run --build-tags kqueue --timeout=10m --config ./.golangci.yml
Comment thread
klauspost marked this conversation as resolved.

lint-fix: getdeps
@echo "Running $@ check"
@${GOPATH}/bin/golangci-lint cache clean
@${GOPATH}/bin/golangci-lint run --build-tags kqueue --timeout=10m --config ./.golangci.yml --fix

test: lint
Expand Down
3 changes: 2 additions & 1 deletion certs/cert_pool_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import (
func loadSystemRoots() (*x509.CertPool, error) {
const CRYPTENOTFOUND = 0x80092004

store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
rootPtr, _ := syscall.UTF16PtrFromString("ROOT")
Comment thread
klauspost marked this conversation as resolved.
Outdated
store, err := syscall.CertOpenSystemStore(0, rootPtr)
if err != nil {
return nil, err
}
Expand Down
10 changes: 10 additions & 0 deletions certs/certificate2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"path/filepath"
"reflect"
"runtime"
"testing"
"time"
)
Expand Down Expand Up @@ -89,6 +90,9 @@ func TestCertificate2_AutoReload(t *testing.T) {
}

func TestCertificate2_AutoReloadSymlink(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlinks require admin on Windows")
}
testCertificate2AutoReload(t, true)
}

Expand Down Expand Up @@ -177,6 +181,9 @@ func TestCertificate2_AutoReloadCertFileOnly(t *testing.T) {
}

func TestCertificate2_AutoReloadCertFileOnlySymlink(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlinks require admin on Windows")
}
testCertificate2AutoReloadCertFileOnly(t, true)
}

Expand Down Expand Up @@ -216,6 +223,9 @@ func TestCertificate2_InvalidReloadIgnored(t *testing.T) {
}

func TestCertificate2_InvalidReloadIgnoredSymlink(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlinks require admin on Windows")
}
testCertificate2InvalidReloadIgnored(t, true)
}

Expand Down
2 changes: 1 addition & 1 deletion certs/event_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ package certs
import "github.com/rjeczalik/notify"

// eventWrite contains the notify events that will cause a write
var eventWrite = []notify.Event{notify.Create, notify.Write}
var eventWrite = []notify.Event{notify.Create, notify.Write, notify.Rename}
72 changes: 0 additions & 72 deletions certs/manager2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ package certs
import (
"context"
"crypto/tls"
"reflect"
"syscall"
"testing"
"time"
)
Expand Down Expand Up @@ -69,76 +67,6 @@ func TestManager2_CloseMultipleTimes(t *testing.T) {
mgr.Close()
}

func TestManager2_ReloadOnSIGHUP(t *testing.T) {
callCount := 0
loadCerts := func() ([]*Certificate2, error) {
certFile, keyFile := "public.crt", "private.key"
if callCount%2 == 1 {
certFile, keyFile = "new-public.crt", "new-private.key"
}
callCount++

cert, err := NewCertificate2(certFile, keyFile)
if err != nil {
return nil, err
}
return []*Certificate2{cert}, nil
}

mgr, err := NewManager2(loadCerts)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
defer mgr.Close()

originalCerts := mgr.certs.Load()
originalCert := (*originalCerts)[0].Load()

updateReloadWithWait(t, mgr, func() {
if err := syscall.Kill(syscall.Getpid(), syscall.SIGHUP); err != nil {
t.Fatalf("Failed to send SIGHUP: %v", err)
}
})

newCerts := mgr.certs.Load()
newCert := (*newCerts)[0].Load()

if reflect.DeepEqual(originalCert.Certificate, newCert.Certificate) {
t.Error("Expected certificates to be reloaded after SIGHUP")
}

expectedCert, err := tls.LoadX509KeyPair("new-public.crt", "new-private.key")
if err != nil {
t.Fatalf("Failed to load expected certificate: %v", err)
}

if !reflect.DeepEqual(newCert.Certificate, expectedCert.Certificate) {
t.Error("Reloaded certificate doesn't match expected certificate")
}
}

func updateReloadWithWait(t *testing.T, mgr *Manager2, update func()) {
done := make(chan struct{})
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

unsub := mgr.Subscribe(func(c *Certificate2) {
if c == nil {
close(done)
}
})
defer unsub()

update()

select {
case <-done:
// expected result
case <-ctx.Done():
t.Error("Timeout waiting for certificate update")
}
}

// TestManager2_NoCertificates tests GetCertificate with no loaded certificates.
func TestManager2_NoCertificates(t *testing.T) {
loadCerts := func() ([]*Certificate2, error) {
Expand Down
99 changes: 99 additions & 0 deletions certs/manager2_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) 2025 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

//go:build unix

package certs

import (
"context"
"crypto/tls"
"reflect"
"syscall"
"testing"
"time"
)

func TestManager2_ReloadOnSIGHUP(t *testing.T) {
callCount := 0
loadCerts := func() ([]*Certificate2, error) {
certFile, keyFile := "public.crt", "private.key"
if callCount%2 == 1 {
certFile, keyFile = "new-public.crt", "new-private.key"
}
callCount++

cert, err := NewCertificate2(certFile, keyFile)
if err != nil {
return nil, err
}
return []*Certificate2{cert}, nil
}

mgr, err := NewManager2(loadCerts)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
defer mgr.Close()

originalCerts := mgr.certs.Load()
originalCert := (*originalCerts)[0].Load()

updateReloadWithWait(t, mgr, func() {
if err := syscall.Kill(syscall.Getpid(), syscall.SIGHUP); err != nil {
t.Fatalf("Failed to send SIGHUP: %v", err)
}
})

newCerts := mgr.certs.Load()
newCert := (*newCerts)[0].Load()

if reflect.DeepEqual(originalCert.Certificate, newCert.Certificate) {
t.Error("Expected certificates to be reloaded after SIGHUP")
}

expectedCert, err := tls.LoadX509KeyPair("new-public.crt", "new-private.key")
if err != nil {
t.Fatalf("Failed to load expected certificate: %v", err)
}

if !reflect.DeepEqual(newCert.Certificate, expectedCert.Certificate) {
t.Error("Reloaded certificate doesn't match expected certificate")
}
}

func updateReloadWithWait(t *testing.T, mgr *Manager2, update func()) {
done := make(chan struct{})
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

unsub := mgr.Subscribe(func(c *Certificate2) {
if c == nil {
close(done)
}
})
defer unsub()

update()

select {
case <-done:
// expected result
case <-ctx.Done():
t.Error("Timeout waiting for certificate update")
}
}
Loading
Loading