mirror of
https://github.com/hanzoai/compute.git
synced 2026-10-30 14:50:41 +00:00
Adds .hanzo/workflows/cicd.yml calling the canonical pipeline and drops the GitHub-hosted workflows it replaces. Lands here because this is where the repo is written — git.hanzo.ai is upstream and github.com is its push mirror, so the same change made on github.com never reaches the runner fleet.
63 lines
2.2 KiB
Go
63 lines
2.2 KiB
Go
// Copyright 2025 Hanzo Industries Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/digitalocean/godo"
|
|
"github.com/hanzoai/money"
|
|
)
|
|
|
|
// doCostReader reads a BYOC DigitalOcean account's spend via the customer balance
|
|
// endpoint (GET /v2/customers/my/balance → month_to_date_usage). The balance API is
|
|
// account-wide from the API token, so no extra cost-read scope is needed beyond the
|
|
// token already stored to manage machines — the token needs read access to billing,
|
|
// which the standard DO API token has.
|
|
type doCostReader struct {
|
|
client *godo.Client
|
|
}
|
|
|
|
// newDOCostReader builds the reader from the BYOC token (clientSecret, or clientId
|
|
// as the DO convention allows the token in either field). An empty token yields
|
|
// ErrCostUnavailable so an unconfigured provider is skipped, never guessed.
|
|
func newDOCostReader(clientId, clientSecret string) (CloudCostReader, error) {
|
|
token := strings.TrimSpace(clientSecret)
|
|
if token == "" {
|
|
token = strings.TrimSpace(clientId)
|
|
}
|
|
if token == "" {
|
|
return nil, ErrCostUnavailable
|
|
}
|
|
return &doCostReader{client: newDOClient(token, nil)}, nil
|
|
}
|
|
|
|
// MonthToDateCents reads the account's month-to-date usage. The balance endpoint is
|
|
// always current, so `now` is unused (the DO API has no historical-month selector).
|
|
func (r *doCostReader) MonthToDateCents(ctx context.Context, _ time.Time) (int64, error) {
|
|
bal, _, err := r.client.Balance.Get(ctx)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("do cost: get balance: %w", err)
|
|
}
|
|
cents, err := money.ParseCents(bal.MonthToDateUsage)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("do cost: month-to-date usage %q: %w", bal.MonthToDateUsage, err)
|
|
}
|
|
return cents, nil
|
|
}
|