diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e59906b..1975cbe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,8 +7,8 @@ jobs: name: Mod runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: "./go.mod" - run: go mod download @@ -20,8 +20,8 @@ jobs: env: CGO_ENABLED: "0" steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: "./go.mod" - run: go build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2ce103..542f27a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,17 +8,22 @@ on: - '**' env: - IMAGE_NAME: dev-ops-bot + IMAGE_NAME: sakura-dev-ops-bot + REGISTRY: ghcr.io jobs: goreleaser: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 + with: + fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: "./go.mod" @@ -34,37 +39,33 @@ jobs: name: Build Docker Image runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set IMAGE_TAG env - run: echo "IMAGE_TAG=$(echo ${GITHUB_REF:11})" >> $GITHUB_ENV + - name: Checkout + uses: actions/checkout@v6 + + - name: Extract version from tag + id: version + run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v3 - with: - platforms: all + - name: Set up Docker Buildx - id: buildx uses: docker/setup-buildx-action@v3 - - name: Show available platforms - run: echo ${{ steps.buildx.outputs.platforms }} - - name: Get lowercased owner name - run: echo "REPO_OWNER=${GITHUB_REPOSITORY_OWNER@L}" >> $GITHUB_ENV - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: - registry: ghcr.io - username: ${{ env.REPO_OWNER }} + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push + - name: Build and push Docker image uses: docker/build-push-action@v6 with: context: . push: true platforms: linux/amd64,linux/arm64 - build-args: | - VERSION=${{ env.IMAGE_TAG }} + build-args: VERSION=${{ steps.version.outputs.tag }} tags: | - ghcr.io/${{ env.REPO_OWNER }}/${{ env.IMAGE_NAME }}:latest - ghcr.io/${{ env.REPO_OWNER }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} + ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }} diff --git a/.goreleaser.yml b/.goreleaser.yml index b00e99c..824b09f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,16 +1,16 @@ # .goreleaser.yml -project_name: DevOpsBot +project_name: sakura-DevOpsBot before: hooks: - go mod download builds: - - binary: devops-bot + - binary: sakura-devops-bot env: - CGO_ENABLED=0 ldflags: - -s - -w - - -X github.com/traPtitech/DevOpsBot/pkg/utils.version={{.Version}} + - -X github.com/traPtitech/sakura-DevOpsBot/pkg/utils.version={{.Version}} goos: - darwin - linux diff --git a/Dockerfile b/Dockerfile index 96b7f24..6eb9bc2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM --platform=$BUILDPLATFORM golang:1-alpine AS builder -ENV CGO_ENABLED 0 +ENV CGO_ENABLED=0 WORKDIR /work @@ -16,7 +16,7 @@ ARG TARGETARCH ENV GOOS=$TARGETOS ENV GOARCH=$TARGETARCH RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \ - go build -o /dev-ops-bot -ldflags="-s -w -X github.com/traPtitech/DevOpsBot/pkg/utils.version=$VERSION" . + go build -o /dev-ops-bot -ldflags="-s -w -X github.com/traPtitech/sakura-DevOpsBot/pkg/utils.version=$VERSION" . FROM alpine:3 diff --git a/cmd/root.go b/cmd/root.go index 148387f..68cd6b6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,17 +6,17 @@ import ( "github.com/spf13/cobra" - "github.com/traPtitech/DevOpsBot/pkg/bot" - "github.com/traPtitech/DevOpsBot/pkg/utils" + "github.com/traPtitech/sakura-DevOpsBot/pkg/bot" + "github.com/traPtitech/sakura-DevOpsBot/pkg/utils" ) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "DevOpsBot", + Use: "sakura-DevOpsBot", Short: "A ChatOps bot for executing arbitrary shell commands.", SilenceUsage: true, PreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("DevOpsBot v%s initializing\n", utils.Version()) + fmt.Printf("sakura-DevOpsBot v%s initializing\n", utils.Version()) }, RunE: func(cmd *cobra.Command, args []string) error { return bot.Run(cmd.Context()) diff --git a/cmd/server.go b/cmd/server.go index 2203237..60f360a 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -3,12 +3,12 @@ package cmd import ( "github.com/spf13/cobra" - "github.com/traPtitech/DevOpsBot/pkg/server" + "github.com/traPtitech/sakura-DevOpsBot/pkg/server" ) var serverCmd = &cobra.Command{ Use: "server", - Short: "ConoHa server manipulation", + Short: "Sakura server manipulation", SilenceUsage: true, // Do not display command usage when RunE returns error RunE: func(cmd *cobra.Command, args []string) error { s, err := server.Compile() diff --git a/go.mod b/go.mod index 4bd1c40..320f147 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/traPtitech/DevOpsBot +module github.com/traPtitech/sakura-DevOpsBot go 1.22 @@ -14,7 +14,6 @@ require ( github.com/traPtitech/go-traq v0.0.0-20240725071454-97c7b85dc879 github.com/traPtitech/traq-ws-bot v1.2.1 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.7.0 ) require ( diff --git a/go.sum b/go.sum index e71319e..c2cdfeb 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,6 @@ golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/main.go b/main.go index b0099a1..ccf0114 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/traPtitech/DevOpsBot/cmd" + "github.com/traPtitech/sakura-DevOpsBot/cmd" ) func main() { diff --git a/pkg/bot/bot.go b/pkg/bot/bot.go index 987c3f3..0110d7f 100644 --- a/pkg/bot/bot.go +++ b/pkg/bot/bot.go @@ -3,13 +3,14 @@ package bot import ( "context" "fmt" - "github.com/traPtitech/DevOpsBot/pkg/bot/slack" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" + + "github.com/traPtitech/sakura-DevOpsBot/pkg/bot/slack" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" "go.uber.org/zap" - "github.com/traPtitech/DevOpsBot/pkg/bot/traq" + "github.com/traPtitech/sakura-DevOpsBot/pkg/bot/traq" ) func Run(ctx context.Context) error { diff --git a/pkg/bot/command.go b/pkg/bot/command.go index 03c77b6..5b5febe 100644 --- a/pkg/bot/command.go +++ b/pkg/bot/command.go @@ -11,9 +11,9 @@ import ( "github.com/samber/lo" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" - "github.com/traPtitech/DevOpsBot/pkg/utils" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" + "github.com/traPtitech/sakura-DevOpsBot/pkg/utils" ) var ( diff --git a/pkg/bot/help.go b/pkg/bot/help.go index 9db79d1..f51d2f7 100644 --- a/pkg/bot/help.go +++ b/pkg/bot/help.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" - "github.com/traPtitech/DevOpsBot/pkg/utils" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" + "github.com/traPtitech/sakura-DevOpsBot/pkg/utils" ) var _ domain.Command = (*HelpCommand)(nil) @@ -21,7 +21,7 @@ func (h *HelpCommand) Execute(ctx domain.Context) error { // Root usage if len(args) == 0 { - lines = append(lines, fmt.Sprintf("## DevOpsBot v%s", utils.Version())) + lines = append(lines, fmt.Sprintf("## sakura-DevOpsBot v%s", utils.Version())) lines = append(lines, "") lines = append(lines, h.root.HelpMessage(0, true)...) lines = append(lines, "") diff --git a/pkg/bot/slack/bot.go b/pkg/bot/slack/bot.go index 3c88848..379acc3 100644 --- a/pkg/bot/slack/bot.go +++ b/pkg/bot/slack/bot.go @@ -3,17 +3,18 @@ package slack import ( "context" "fmt" + "log/slog" + "regexp" + "strings" + "github.com/kballard/go-shellquote" "github.com/samber/lo" "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" "github.com/slack-go/slack/socketmode" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" "go.uber.org/zap" - "log/slog" - "regexp" - "strings" ) const slashPrefix = "/" diff --git a/pkg/bot/slack/context.go b/pkg/bot/slack/context.go index 58205f8..9a190d4 100644 --- a/pkg/bot/slack/context.go +++ b/pkg/bot/slack/context.go @@ -2,12 +2,13 @@ package slack import ( "context" + "strings" + "github.com/slack-go/slack" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" - "github.com/traPtitech/DevOpsBot/pkg/utils" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" + "github.com/traPtitech/sakura-DevOpsBot/pkg/utils" "go.uber.org/zap" - "strings" ) type slackContext struct { diff --git a/pkg/bot/traq/bot.go b/pkg/bot/traq/bot.go index 04cdaba..9318c9b 100644 --- a/pkg/bot/traq/bot.go +++ b/pkg/bot/traq/bot.go @@ -3,15 +3,16 @@ package traq import ( "context" "fmt" + "strings" + "github.com/kballard/go-shellquote" "github.com/samber/lo" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" "github.com/traPtitech/go-traq" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" traqwsbot "github.com/traPtitech/traq-ws-bot" "github.com/traPtitech/traq-ws-bot/payload" "go.uber.org/zap" - "strings" ) type traqBot struct { diff --git a/pkg/bot/traq/context.go b/pkg/bot/traq/context.go index 163d8ff..bafa612 100644 --- a/pkg/bot/traq/context.go +++ b/pkg/bot/traq/context.go @@ -2,15 +2,16 @@ package traq import ( "context" - "github.com/traPtitech/DevOpsBot/pkg/config" - "github.com/traPtitech/DevOpsBot/pkg/domain" + "strings" + + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/domain" "github.com/traPtitech/traq-ws-bot/payload" "go.uber.org/zap" - "strings" "github.com/traPtitech/go-traq" - "github.com/traPtitech/DevOpsBot/pkg/utils" + "github.com/traPtitech/sakura-DevOpsBot/pkg/utils" ) type traqContext struct { diff --git a/pkg/config/config.go b/pkg/config/config.go index 48c4a69..057f9bb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -102,15 +102,12 @@ type CommandConfig struct { } type ServersConfig struct { - Conoha struct { - Origin struct { - Identity string `mapstructure:"identity" yaml:"identity"` - Compute string `mapstructure:"compute" yaml:"compute"` - } `mapstructure:"origin" yaml:"origin"` - Username string `mapstructure:"username" yaml:"username"` - Password string `mapstructure:"password" yaml:"password"` - TenantID string `mapstructure:"tenantID" yaml:"tenantID"` - } `mapstructure:"conoha" yaml:"conoha"` + Sakura struct { + Origin string `mapstructure:"origin" yaml:"origin"` + // ServersAPIURLPath must start with "/" + ServersAPIURLPath string `mapstructure:"serversAPIURLPath" yaml:"serversAPIURLPath"` + BearerToken string `mapstructure:"bearerToken" yaml:"bearerToken"` + } `mapstructure:"sakura" yaml:"sakura"` } func init() { @@ -143,11 +140,9 @@ func init() { viper.SetDefault("templates", nil) viper.SetDefault("commands", nil) - viper.SetDefault("servers.conoha.origin.identity", "https://identity.tyo1.conoha.io/") - viper.SetDefault("servers.conoha.origin.compute", "https://compute.tyo1.conoha.io/") - viper.SetDefault("servers.conoha.username", "") - viper.SetDefault("servers.conoha.password", "") - viper.SetDefault("servers.conoha.tenantID", "") + viper.SetDefault("servers.sakura.origin", "https://secure.sakura.ad.jp") + viper.SetDefault("servers.sakura.serversAPIURLPath", "/vps/api/v7/servers") + viper.SetDefault("servers.sakura.bearerToken", "") } func init() { diff --git a/pkg/server/hosts.go b/pkg/server/hosts.go index 6080abc..c56bb86 100644 --- a/pkg/server/hosts.go +++ b/pkg/server/hosts.go @@ -6,140 +6,206 @@ import ( "io" "log" "net/http" + "os" "sort" - "sync" + "strings" + "text/tabwriter" "github.com/dghubble/sling" - "github.com/traPtitech/DevOpsBot/pkg/config" - "golang.org/x/sync/errgroup" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" ) type hostsCommand struct { } +type params struct { + Page int64 `json:"page,omitempty"` + PerPage int64 `json:"per_page,omitempty"` + ID string `json:"id,omitempty"` + Switch int64 `json:"switch,omitempty"` + ZoneCode string `json:"zone_code,omitempty"` + ServiceType string `json:"service_type,omitempty"` + IPv4Address string `json:"ipv4_address,omitempty"` + MonitoringResourceID string `json:"monitoring_resource_id,omitempty"` + Sort string `json:"sort,omitempty"` + Search string `json:"search,omitempty"` +} + type serversResponse struct { - Servers []struct { - ID string `json:"id"` - Links []struct { - Href string `json:"href"` - Rel string `json:"rel"` - } `json:"links"` - Name string `json:"name"` - } `json:"servers"` + Results []serverResponse `json:"results"` + Count int64 `json:"count"` + Next *string `json:"next,omitempty"` + Previous *string `json:"previous,omitempty"` } type serverResponse struct { - Server struct { - ID string `json:"id"` - Status string `json:"status"` - OSEXTSRVATTRHost string `json:"OS-EXT-SRV-ATTR:host"` - Metadata struct { - InstanceNameTag string `json:"instance_name_tag"` - } `json:"metadata"` - } `json:"server"` + ID int64 `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + ServiceType string `json:"service_type"` + ServiceStatus string `json:"service_status"` + CPUCores int64 `json:"cpu_cores"` + MemoryMiB int64 `json:"memory_mebibytes"` + Storage []struct { + Port int64 `json:"port"` + Type string `json:"type"` + SizeGiB int64 `json:"size_gibibytes"` + } `json:"storage"` + Zone struct { + Code string `json:"code"` + Name string `json:"name"` + } `json:"zone"` + Options []string `json:"options"` + Version string `json:"version"` + Ipv4 struct { + Address string `json:"address"` + Netmask string `json:"netmask"` + Gateway string `json:"gateway"` + NameServers []string `json:"nameservers"` + HostName string `json:"hostname"` + PTR string `json:"ptr"` + } `json:"ipv4"` + Ipv6 struct { + Address *string `json:"address,omitempty"` + PrefixLen *int64 `json:"prefixlen,omitempty"` + Gateway *string `json:"gateway,omitempty"` + NameServers []string `json:"nameservers"` + HostName *string `json:"hostname,omitempty"` + PTR *string `json:"ptr,omitempty"` + } `json:"ipv6"` + Contract struct { + PlanCode int64 `json:"plan_code"` + PlanName string `json:"plan_name"` + ServiceCode string `json:"service_code"` + } `json:"contract"` + PowerStatus string `json:"power_status"` + MigrationStatus *string `json:"migration_status,omitempty"` } type resultData struct { - ID string - Host string - Name string + ID int64 + Zone string + Name string + Ipv4 string + Ipv6 string + CpuCores int64 + MemoryMiB int64 + Storage []struct { + Size int64 + Type string + } } func (sc *hostsCommand) Execute(args []string) error { - token, err := getConohaAPIToken() - if err != nil { - return fmt.Errorf("failed to get conoha api token: %w", err) - } - - req, err := sling.New(). - Base(config.C.Servers.Conoha.Origin.Compute). - Get(fmt.Sprintf("v2/%s/servers", config.C.Servers.Conoha.TenantID)). - Set("Accept", "application/json"). - Set("X-Auth-Token", token). - Request() - if err != nil { - return fmt.Errorf("failed to create restart request: %w", err) + if len(strings.TrimSpace(config.C.Servers.Sakura.BearerToken)) == 0 { + return fmt.Errorf("API token has not been set yet") } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return fmt.Errorf("failed to post restart request: %w", err) - } - defer resp.Body.Close() - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("invalid status code: %s (expected: 200)", resp.Status) - } - - var response serversResponse - if err := json.Unmarshal(respBody, &response); err != nil { - return fmt.Errorf("failed to unmarshal response body: %w", err) + if !strings.HasPrefix(config.C.Servers.Sakura.ServersAPIURLPath, "/") { + config.C.Servers.Sakura.ServersAPIURLPath = "/" + config.C.Servers.Sakura.ServersAPIURLPath } + datasPerPage := 100 servers := []resultData{} - - eg := errgroup.Group{} - mu := sync.Mutex{} - for _, server := range response.Servers { - eg.Go(func() error { - req, err := sling.New(). - Base(config.C.Servers.Conoha.Origin.Compute). - Get(fmt.Sprintf("v2/%s/servers/%s", config.C.Servers.Conoha.TenantID, server.ID)). - Set("Accept", "application/json"). - Set("X-Auth-Token", token). - Request() - if err != nil { - return fmt.Errorf("failed to create restart request: %w", err) + page := 1 + maxPages := 10 + + // Fetch all pages of results + for page <= maxPages { + req, err := sling.New(). + Base(config.C.Servers.Sakura.Origin). + Get(config.C.Servers.Sakura.ServersAPIURLPath). + Add("Authorization", "Bearer "+config.C.Servers.Sakura.BearerToken). + QueryStruct(¶ms{PerPage: int64(datasPerPage), Page: int64(page)}). + Request() + if err != nil { + return fmt.Errorf("failed to create hosts request: %w", err) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to get hosts: %w", err) + } + + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + return fmt.Errorf("invalid status code: %s (expected: 200)", resp.Status) + } + + respBody, err := io.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + + var response serversResponse + if err := json.Unmarshal(respBody, &response); err != nil { + return fmt.Errorf("failed to unmarshal response body: %w", err) + } + + for _, server := range response.Results { + serverData := resultData{ + ID: server.ID, + Zone: server.Zone.Name, + Name: server.Name, + Ipv4: server.Ipv4.Address, + CpuCores: server.CPUCores, + MemoryMiB: server.MemoryMiB, } - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return fmt.Errorf("failed to post restart request: %w", err) - } - defer resp.Body.Close() - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) + if server.Ipv6.Address != nil { + serverData.Ipv6 = *server.Ipv6.Address + } else { + serverData.Ipv6 = "null" } - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("invalid status code: %s (expected: 200)", resp.Status) + for _, storage := range server.Storage { + serverData.Storage = append(serverData.Storage, struct { + Size int64 + Type string + }{storage.SizeGiB, storage.Type}) } - - var response serverResponse - if err := json.Unmarshal(respBody, &response); err != nil { - return fmt.Errorf("failed to unmarshal response body: %w", err) - } - - mu.Lock() - servers = append(servers, resultData{ - ID: response.Server.ID, - Host: response.Server.OSEXTSRVATTRHost, - Name: response.Server.Metadata.InstanceNameTag, - }) - mu.Unlock() - - return nil - }) - } - - if err := eg.Wait(); err != nil { - return fmt.Errorf("error in goroutines: %w", err) + servers = append(servers, serverData) + } + + // Check if there are more pages + if response.Next == nil { + break + } + page++ } sort.Slice(servers, func(i, j int) bool { return servers[i].Name < servers[j].Name }) + // Create tabwriter for aligned output + // Parameters: output, minwidth, tabwidth, padding, padchar, flags + w := tabwriter.NewWriter(os.Stderr, 0, 4, 2, ' ', 0) + fmt.Fprintln(w, "Server Name\tServer ID\tZone Name\tCPU Cores\tIPv4 Address\tIPv6 Address\tMemory (MiB)\tStorage") + fmt.Fprintln(w, "-----------\t---------\t---------\t---------\t------------\t------------\t------------\t-------") + for _, server := range servers { - log.Printf("%s: %s\n", server.Name, server.Host) + storageInfo := "" + for i, storage := range server.Storage { + if i > 0 { + storageInfo += ", " + } + storageInfo += fmt.Sprintf("%dGiB/%s", storage.Size, storage.Type) + } + + fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%s\t%s\t%d\t%s\n", + server.Name, + server.ID, + server.Zone, + server.CpuCores, + server.Ipv4, + server.Ipv6, + server.MemoryMiB, + storageInfo, + ) } + w.Flush() + log.Printf("Total servers: %d", len(servers)) + return nil } diff --git a/pkg/server/restart.go b/pkg/server/restart.go index fa2cd22..28c853b 100644 --- a/pkg/server/restart.go +++ b/pkg/server/restart.go @@ -2,50 +2,40 @@ package server import ( "fmt" - "io" "log" "net/http" + "strconv" + "strings" "github.com/dghubble/sling" - "github.com/samber/lo" - "github.com/traPtitech/DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" ) type restartCommand struct { } -type m map[string]any - func (sc *restartCommand) Execute(args []string) error { - if len(args) < 1 { + if len(args) != 1 { return fmt.Errorf("invalid arguments, expected server id") } - serverID := args[0] - args = args[1:] - - if len(args) < 1 { - return fmt.Errorf("invalid arguments, expected restart type (SOFT or HARD)") + serverID, err := strconv.Atoi(args[0]) + if err != nil { + return fmt.Errorf("invalid arguments, server id must be an integer: %q", args[0]) } - // args == [SOFT|HARD] - restartType := args[0] - - if !lo.Contains([]string{"SOFT", "HARD"}, restartType) { - return fmt.Errorf("unknown restart type: %s", restartType) + if len(strings.TrimSpace(config.C.Servers.Sakura.BearerToken)) == 0 { + return fmt.Errorf("API token has not been set yet") } - token, err := getConohaAPIToken() - if err != nil { - return fmt.Errorf("failed to get conoha api token: %w", err) + if !strings.HasPrefix(config.C.Servers.Sakura.ServersAPIURLPath, "/") { + config.C.Servers.Sakura.ServersAPIURLPath = "/" + config.C.Servers.Sakura.ServersAPIURLPath } req, err := sling.New(). - Base(config.C.Servers.Conoha.Origin.Compute). - Post(fmt.Sprintf("v2/%s/servers/%s/action", config.C.Servers.Conoha.TenantID, serverID)). - BodyJSON(m{"reboot": m{"type": args[0]}}). - Set("Accept", "application/json"). - Set("X-Auth-Token", token). + Base(config.C.Servers.Sakura.Origin). + Post(fmt.Sprintf("%s/%d/force-reboot", config.C.Servers.Sakura.ServersAPIURLPath, serverID)). + Add("Authorization", "Bearer "+config.C.Servers.Sakura.BearerToken). Request() if err != nil { return fmt.Errorf("failed to create restart request: %w", err) @@ -58,25 +48,11 @@ func (sc *restartCommand) Execute(args []string) error { } defer resp.Body.Close() - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %w", err) - } - - logStr := fmt.Sprintf(`Request -- URL: %s -- RestartType: %s - -Response -- Header: %+v -- Body: %s -- Status: %s (Expected: 202) -`, req.URL.String(), restartType, resp.Header, string(respBody), resp.Status) - log.Println(logStr) - if resp.StatusCode != http.StatusAccepted { return fmt.Errorf("incorrect status code: %s", resp.Status) } + log.Printf("Server restart requested successfully: ServerID=%d, Status=%s", serverID, resp.Status) + return nil } diff --git a/pkg/server/server.go b/pkg/server/server.go index 068d912..b041a68 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -9,12 +9,13 @@ type ServersCommand struct { Commands map[string]command } +// CLIにコマンドを登録 func Compile() (*ServersCommand, error) { cmd := &ServersCommand{} cmd.Commands = make(map[string]command) cmd.Commands["restart"] = &restartCommand{} - cmd.Commands["hosts"] = &hostsCommand{} + cmd.Commands["info"] = &hostsCommand{} return cmd, nil } diff --git a/pkg/server/token.go b/pkg/server/token.go index d365b56..c3871af 100644 --- a/pkg/server/token.go +++ b/pkg/server/token.go @@ -1,5 +1,6 @@ package server +/* import ( "encoding/json" "fmt" @@ -7,7 +8,7 @@ import ( "net/http" "github.com/dghubble/sling" - "github.com/traPtitech/DevOpsBot/pkg/config" + "github.com/traPtitech/sakura-DevOpsBot/pkg/config" ) func getConohaAPIToken() (string, error) { @@ -70,3 +71,4 @@ func getConohaAPIToken() (string, error) { return responseJson.Access.Token.Id, nil } +*/ \ No newline at end of file