mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
fix(terraform): drop litellm_key from state on 404 instead of failing the plan (#40443)
Co-authored-by: yassin <yassin@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
ea106fd8be
commit
5264f8ed48
3 changed files with 60 additions and 1 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
|
@ -19,6 +20,20 @@ type Client struct {
|
|||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
}
|
||||
|
||||
func (e *apiError) Error() string {
|
||||
return fmt.Sprintf("API request failed with status code %d: %s", e.StatusCode, e.Body)
|
||||
}
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
var apiErr *apiError
|
||||
return errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound
|
||||
}
|
||||
|
||||
func NewClient(apiBase, apiKey string, insecureSkipVerify bool) *Client {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify},
|
||||
|
|
@ -57,6 +72,9 @@ func (c *Client) CreateKey(key *Key) (*Key, error) {
|
|||
|
||||
func (c *Client) GetKey(keyID string) (*Key, error) {
|
||||
resp, err := c.sendRequest("GET", fmt.Sprintf("/key/info?key=%s", keyID), nil)
|
||||
if isNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -377,7 +395,7 @@ func (c *Client) sendRequest(method, path string, body interface{}) (map[string]
|
|||
log.Printf("Response body: %s", c.redactSensitiveData(string(bodyBytes)))
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode, string(bodyBytes))
|
||||
return nil, &apiError{StatusCode: resp.StatusCode, Body: string(bodyBytes)}
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/go-cty/cty"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
|
||||
|
|
@ -297,6 +298,7 @@ func resourceKeyRead(ctx context.Context, d *schema.ResourceData, m interface{})
|
|||
}
|
||||
|
||||
if key == nil {
|
||||
log.Printf("[WARN] Key %s not found, removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,6 +394,45 @@ func TestGetKeyUnwrapsInfoEnvelope(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResourceKeyReadDropsMissingKeyFromState(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error":{"message":"Key not found in database","type":"not_found_error","param":"key","code":"404"}}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := newKeyResourceData(t, map[string]interface{}{"key_alias": "stale"})
|
||||
d.SetId("deleted-out-of-band")
|
||||
|
||||
diags := resourceKeyRead(context.Background(), d, NewClient(srv.URL, "test-key", true))
|
||||
if diags.HasError() {
|
||||
t.Fatalf("read of a missing key must not error, got: %v", diags)
|
||||
}
|
||||
if d.Id() != "" {
|
||||
t.Errorf("Id = %q, want empty so Terraform plans a recreate", d.Id())
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceKeyReadStillFailsOnNon404Errors(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error":{"message":"db down"}}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := newKeyResourceData(t, map[string]interface{}{"key_alias": "live"})
|
||||
d.SetId("still-exists")
|
||||
|
||||
diags := resourceKeyRead(context.Background(), d, NewClient(srv.URL, "test-key", true))
|
||||
if !diags.HasError() {
|
||||
t.Fatal("a 500 from /key/info must surface as an error, not be treated as a deleted key")
|
||||
}
|
||||
if d.Id() != "still-exists" {
|
||||
t.Errorf("Id = %q, want unchanged on a transient error", d.Id())
|
||||
}
|
||||
}
|
||||
|
||||
// fakeKeyProxy serves /key/info from stored metadata and applies /key/update
|
||||
// the way the proxy does: an absent "metadata" keeps the stored map, a
|
||||
// present one replaces it wholesale.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue