Skip to content

Conversation

@blancnoir256
Copy link
Collaborator

DevOpsBotをさくらVPSのAPIに対応させました。

CIがまだ整備できていません。
かつまだテストしてないのでDraftです。

このあとローカルテストします。

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates DevOpsBot from ConoHa VPS API to Sakura VPS API. The implementation includes updating the module name, replacing the authentication mechanism from token-based to Bearer token authentication, and adapting all API endpoints and response structures to match the Sakura VPS API specification.

Changes:

  • Renamed module from github.com/traPtitech/DevOpsBot to github.com/traPtitech/sakura-DevOpsBot
  • Replaced ConoHa API configuration and authentication with Sakura VPS Bearer token authentication
  • Updated server operations (restart, hosts) to use Sakura VPS API endpoints and response structures
  • Removed unused dependencies (golang.org/x/sync) and simplified the restart command to remove the soft/hard restart type distinction

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
go.mod Updated module name and removed golang.org/x/sync dependency
go.sum Updated checksums for dependency changes
main.go Updated import path to use sakura-DevOpsBot module name
Dockerfile Updated build command to reference new module name
cmd/root.go Updated import paths
cmd/server.go Updated import paths
pkg/config/config.go Replaced ConoHa configuration with Sakura VPS configuration including origin, API path, and bearer token
pkg/server/token.go Commented out entire ConoHa token authentication implementation
pkg/server/server.go Added Japanese comment for command registration
pkg/server/restart.go Refactored to use Sakura VPS force-reboot endpoint, simplified to single server ID argument, removed soft/hard restart types
pkg/server/hosts.go Completely rewritten to match Sakura VPS API response structure, removed parallel goroutine fetching, updated logging format
pkg/bot/bot.go Updated import paths
pkg/bot/command.go Updated import paths
pkg/bot/help.go Updated import paths
pkg/bot/traq/bot.go Updated import paths, reordered imports
pkg/bot/traq/context.go Updated import paths, reordered imports
pkg/bot/slack/bot.go Updated import paths, reordered imports
pkg/bot/slack/context.go Updated import paths, reordered imports

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 48 to 56
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)
`, req.URL.String(), resp.Header, string(respBody), resp.Status)
log.Println(logStr)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the full response body and headers may expose sensitive information. Consider reviewing what information is included in the response and whether it's safe to log it. If the response contains any authentication tokens, server credentials, or other sensitive data, this could be a security concern.

Copilot uses AI. Check for mistakes.
Name string `json:"name"`
} `json:"servers"`
Results []serverResponse `json:"results"`
Count string `json:"count"`
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Count field is defined as string, but count fields in paginated API responses are typically integers. Verify that the Sakura VPS API actually returns count as a string. If it's a number, this should be changed to int or int64 to match the API specification and avoid potential parsing issues.

Suggested change
Count string `json:"count"`
Count int `json:"count"`

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +33
CPUCores string `json:"cpu_cores"`
MemoryMiB string `json:"memory_mebibytes"`
Storage []struct {
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fields like CPUCores, MemoryMiB, and SizeGiB are defined as strings, but these represent numeric values. While this might be intentional to match the API response format, it makes numeric operations difficult and could lead to confusion. Verify the actual API response format and consider using appropriate numeric types (int, int64) if the API returns numbers, or document why strings are used if it's a deliberate choice.

Suggested change
CPUCores string `json:"cpu_cores"`
MemoryMiB string `json:"memory_mebibytes"`
Storage []struct {
// NOTE: CPUCores, MemoryMiB, and SizeGiB are defined as strings because the upstream API
// encodes these numeric values as JSON strings. Convert to numeric types at use sites
// if arithmetic or comparisons are needed.
CPUCores string `json:"cpu_cores"`
MemoryMiB string `json:"memory_mebibytes"`
Storage []struct {

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +144
CpuCores: server.CPUCores,
MemoryMiB: server.MemoryMiB,
}
if server.Ipv6.Address != nil {
serverData.Ipv6 = *server.Ipv6.Address
} else {
serverData.Ipv6 = "null"
}
for _, storage := range server.Storage {
serverData.Storage = append(serverData.Storage, struct {
Size string
Type string
}{storage.SizeGiB, storage.Type})
}
servers = append(servers, serverData)
}

sort.Slice(servers, func(i, j int) bool {
return servers[i].Name < servers[j].Name
})

log.Printf("Server Name :\t Server ID, \t Zone Name, \t ipv4 address, \t ipv6 address, \t Memory size, \t Storage size(Storage type)")
for _, server := range servers {
log.Printf("%s: %s\n", server.Name, server.Host)
logMsg := fmt.Sprintf("%s :\t %s, \t %s, \t %s, \t %s, \t %sMiB", server.Name, server.ID, server.Zone, server.Ipv4, server.Ipv6, server.MemoryMiB)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resultData struct includes CpuCores field (line 75) and it's populated from the server response (line 121), but this information is never displayed in the log output. The log header on line 142 and the log message on line 144 do not include CPU cores. Either add CPU cores to the output or remove it from the resultData struct if it's not needed.

Copilot uses AI. Check for mistakes.
Comment on lines 18 to 23
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 string `json:"count"`
Next *string `json:"next,omitempty"`
Previous *string `json:"previous,omitempty"`
}
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The serversResponse includes pagination fields (Next, Previous) but the code only fetches the first page of results. If there are more servers than fit in one page, they won't be displayed. Consider either implementing pagination to fetch all servers, or documenting that only the first page is shown.

Copilot uses AI. Check for mistakes.
serverID, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("failed to get conoha api token: %w", err)
return fmt.Errorf("invalid arguments, server id is must be integer")
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message contains a grammatical error. It should say "server id must be an integer" instead of "server id is must be integer".

Suggested change
return fmt.Errorf("invalid arguments, server id is must be integer")
return fmt.Errorf("invalid arguments, server id must be an integer")

Copilot uses AI. Check for mistakes.
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)).
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL construction using fmt.Sprintf concatenates paths that may result in double slashes or incorrect paths. With the default ServersApiURLPath starting with "/" and the format string adding "/%d/force-reboot", you're correctly getting "/vps/api/v7/servers/123/force-reboot". However, this assumes ServersApiURLPath always starts with "/". Consider documenting this requirement or using path.Join to make the construction more robust.

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +87
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).
Base(config.C.Servers.Sakura.Origin).
Get(config.C.Servers.Sakura.ServersApiURLPath).
Add("Authorization", "Bearer "+config.C.Servers.Sakura.BearerToken).
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no validation to check if the BearerToken is empty before making API requests. If the token is not configured, the API call will fail with an authentication error. Consider adding validation to check if the Bearer token is configured and return a clearer error message early in the execution.

Copilot uses AI. Check for mistakes.
Comment on lines 27 to +30
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).
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no validation to check if the BearerToken is empty before making API requests. If the token is not configured, the API call will fail with an authentication error. Consider adding validation to check if the Bearer token is configured and return a clearer error message early in the execution.

Copilot uses AI. Check for mistakes.
return servers[i].Name < servers[j].Name
})

log.Printf("Server Name :\t Server ID, \t Zone Name, \t ipv4 address, \t ipv6 address, \t Memory size, \t Storage size(Storage type)")
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log header line contains grammatical inconsistencies in the column labels. "Memory size" should more specifically be "Memory Size (MiB)" or similar, and "Storage size(Storage type)" has inconsistent spacing. Consider using consistent formatting like "Storage Size (GiB) / Type" or "Storage (Size GiB, Type)".

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants