Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Certificatee uses the HAProxy Data Plane API to update certificates at runtime w
- **Basic authentication**: Authenticate using username/password credentials
- **Automatic retries**: Connections are retried with exponential backoff (default: 3 retries, 1-30s delays)
- **Graceful degradation**: If one HAProxy instance is unreachable, the tool continues updating reachable instances
- **REST API**: Certificates are managed via the `/v3/services/haproxy/runtime/certs` endpoints
- **REST API**: Certificates are managed via the `/v2/services/haproxy/runtime/certs` endpoints

### HAProxy Data Plane API Configuration

Expand Down
10 changes: 5 additions & 5 deletions pkg/haproxy/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (c *Client) ListCertificates() ([]string, error) {
// ListCertificateRefs returns a list of certificate references with both display names and file paths
func (c *Client) ListCertificateRefs() ([]CertificateRef, error) {
// Use storage API endpoint for listing SSL certificates
resp, err := c.doRequest("GET", "/v3/services/haproxy/storage/ssl_certificates", nil, "")
resp, err := c.doRequest("GET", "/v2/services/haproxy/storage/ssl_certificates", nil, "")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -348,7 +348,7 @@ func (c *Client) GetCertificateInfo(certName string) (*CertInfo, error) {
func (c *Client) GetCertificateInfoByPath(filePath, displayName string) (*CertInfo, error) {
// URL-encode the file path for the API request
encodedPath := url.PathEscape(filePath)
path := fmt.Sprintf("/v3/services/haproxy/storage/ssl_certificates/%s", encodedPath)
path := fmt.Sprintf("/v2/services/haproxy/storage/ssl_certificates/%s", encodedPath)
resp, err := c.doRequest("GET", path, nil, "")
if err != nil {
return nil, err
Expand Down Expand Up @@ -454,7 +454,7 @@ func (c *Client) UpdateCertificate(certName, pemData string) error {
}

// Send PUT request to replace certificate
path := fmt.Sprintf("/v3/services/haproxy/runtime/certs/%s", certName)
path := fmt.Sprintf("/v2/services/haproxy/runtime/certs/%s", certName)
resp, err := c.doRequestWithBodyBuffer("PUT", path, buf.Bytes(), writer.FormDataContentType())
if err != nil {
return err
Expand Down Expand Up @@ -490,7 +490,7 @@ func (c *Client) CreateCertificate(certName, pemData string) error {
}

// Send POST request to create certificate
resp, err := c.doRequestWithBodyBuffer("POST", "/v3/services/haproxy/runtime/certs", buf.Bytes(), writer.FormDataContentType())
resp, err := c.doRequestWithBodyBuffer("POST", "/v2/services/haproxy/runtime/certs", buf.Bytes(), writer.FormDataContentType())
if err != nil {
return err
}
Expand All @@ -507,7 +507,7 @@ func (c *Client) CreateCertificate(certName, pemData string) error {

// DeleteCertificate deletes a certificate entry via Data Plane API
func (c *Client) DeleteCertificate(certName string) error {
path := fmt.Sprintf("/v3/services/haproxy/runtime/certs/%s", certName)
path := fmt.Sprintf("/v2/services/haproxy/runtime/certs/%s", certName)
resp, err := c.doRequest("DELETE", path, nil, "")
if err != nil {
return err
Expand Down
14 changes: 7 additions & 7 deletions pkg/haproxy/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ func newMockDataPlaneAPI(t *testing.T) *mockDataPlaneAPI {
return
}

// Try prefix matching for dynamic paths (e.g., /v3/services/haproxy/runtime/certs/example.com.pem)
// Try prefix matching for dynamic paths (e.g., /v2/services/haproxy/runtime/certs/example.com.pem)
for pattern, handler := range m.handlers {
if strings.HasPrefix(key, pattern) {
handler(w, r)
Expand Down Expand Up @@ -506,7 +506,7 @@ func TestListCertificates(t *testing.T) {
mock := newMockDataPlaneAPI(t)
defer mock.Close()

mock.SetHandler("GET", "/v3/services/haproxy/storage/ssl_certificates", func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("GET", "/v2/services/haproxy/storage/ssl_certificates", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(tt.statusCode)
if tt.response != nil {
Expand Down Expand Up @@ -616,7 +616,7 @@ kF7B68QUswmVK4Icz6zBgmo=
mock := newMockDataPlaneAPI(t)
defer mock.Close()

mock.SetHandler("GET", "/v3/services/haproxy/storage/ssl_certificates/"+tt.certPath, func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("GET", "/v2/services/haproxy/storage/ssl_certificates/"+tt.certPath, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.statusCode)
if tt.statusCode == http.StatusOK {
_, _ = w.Write([]byte(tt.pemData))
Expand Down Expand Up @@ -689,7 +689,7 @@ func TestUpdateCertificate(t *testing.T) {
mock := newMockDataPlaneAPI(t)
defer mock.Close()

mock.SetHandler("PUT", "/v3/services/haproxy/runtime/certs/"+tt.certName, func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("PUT", "/v2/services/haproxy/runtime/certs/"+tt.certName, func(w http.ResponseWriter, r *http.Request) {
// Verify content type is multipart
contentType := r.Header.Get("Content-Type")
if !strings.Contains(contentType, "multipart/form-data") {
Expand Down Expand Up @@ -776,7 +776,7 @@ func TestCreateCertificate(t *testing.T) {
mock := newMockDataPlaneAPI(t)
defer mock.Close()

mock.SetHandler("POST", "/v3/services/haproxy/runtime/certs", func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("POST", "/v2/services/haproxy/runtime/certs", func(w http.ResponseWriter, r *http.Request) {
// Verify content type is multipart
contentType := r.Header.Get("Content-Type")
if !strings.Contains(contentType, "multipart/form-data") {
Expand Down Expand Up @@ -861,7 +861,7 @@ func TestDeleteCertificate(t *testing.T) {
mock := newMockDataPlaneAPI(t)
defer mock.Close()

mock.SetHandler("DELETE", "/v3/services/haproxy/runtime/certs/"+tt.certName, func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("DELETE", "/v2/services/haproxy/runtime/certs/"+tt.certName, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(tt.statusCode)
})

Expand Down Expand Up @@ -890,7 +890,7 @@ func TestBasicAuth(t *testing.T) {
defer mock.Close()
mock.SetAuth("admin", "secret")

mock.SetHandler("GET", "/v3/services/haproxy/storage/ssl_certificates", func(w http.ResponseWriter, r *http.Request) {
mock.SetHandler("GET", "/v2/services/haproxy/storage/ssl_certificates", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode([]SSLCertificateEntry{})
Expand Down
10 changes: 5 additions & 5 deletions test/integration/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ EOF

# Wait for API to be ready
local retries=30
while ! curl -sf http://127.0.0.1:5555/v3/services/haproxy/runtime/info -u admin:adminpwd > /dev/null 2>&1; do
while ! curl -sf http://127.0.0.1:5555/v2/services/haproxy/runtime/info -u admin:adminpwd > /dev/null 2>&1; do
retries=$((retries - 1))
if [[ ${retries} -le 0 ]]; then
log_error "Data Plane API failed to start. Logs:"
Expand Down Expand Up @@ -301,25 +301,25 @@ test_api_connectivity() {
# First, check API info endpoint
log_info "Checking API info..."
local info
info=$(curl -s http://127.0.0.1:5555/v3/info -u admin:adminpwd) || true
info=$(curl -s http://127.0.0.1:5555/v2/info -u admin:adminpwd) || true
echo "API Info: ${info}"

# Check available endpoints
log_info "Checking runtime info..."
local runtime_info
runtime_info=$(curl -s http://127.0.0.1:5555/v3/services/haproxy/runtime/info -u admin:adminpwd) || true
runtime_info=$(curl -s http://127.0.0.1:5555/v2/services/haproxy/runtime/info -u admin:adminpwd) || true
echo "Runtime Info: ${runtime_info}"

# Try to list certificates via storage endpoint
log_info "Checking storage certs endpoint..."
local storage_certs
storage_certs=$(curl -s http://127.0.0.1:5555/v3/services/haproxy/storage/ssl_certificates -u admin:adminpwd) || true
storage_certs=$(curl -s http://127.0.0.1:5555/v2/services/haproxy/storage/ssl_certificates -u admin:adminpwd) || true
echo "Storage Certs: ${storage_certs}"

# Check runtime certs endpoint
log_info "Checking runtime certs endpoint..."
local runtime_certs
runtime_certs=$(curl -s http://127.0.0.1:5555/v3/services/haproxy/runtime/certs -u admin:adminpwd) || true
runtime_certs=$(curl -s http://127.0.0.1:5555/v2/services/haproxy/runtime/certs -u admin:adminpwd) || true
echo "Runtime Certs: ${runtime_certs}"

if echo "${runtime_certs}" | grep -q "404"; then
Expand Down
Loading