是否想通过Go编程语言学习使用SoftLayer Cloud API的来龙去脉? 本教程将向您展示如何使用服务,导航和使用数据结构以及建立对象掩码和过滤器,以及如何以简洁明了的方式订购VM等。
SoftLayer-Go是用于Go编程语言的SoftLayer API客户端库。 它是基于描述不同服务端点和数据类型的SoftLayer API元数据自动生成的。
用与实际SoftLayer API导出给用户相同的方式来构造softlayer-go 。 也就是说,它具有一组可反映SoftLayer中数据类型的结构 。 另一组结构反映了SoftLayer中的服务,每个结构都附加了所有预期的方法。 这些服务还有一些其他方法(稍后将介绍),这些方法使您可以指定方法的上下文。
这是库中所有Go软件包的细分:
- 数据类型提供对API返回的所有结构的访问,您将需要传递给API,具体取决于所使用的服务方法。
- 服务是针对具有相应方法的所有服务(以结构形式)。
- 会话包含用于与API端点进行通信的所有传输逻辑; 它同时支持REST和XML-RPC端点。
- sl提供了便捷的功能,用于在数据类型结构中获取和设置值。
- filter用于创建对象过滤器,该对象过滤器指定您要从API取回哪些字段。
- 助手需要做更多复杂的事情,包括多次调用API或常见任务,例如获取数据中心名称的ID。
我们将在随后的部分中详细讨论这些软件包。
安装softlayer-go
在安装库之前,您需要执行以下操作:
- 安装Go
- 获取GOPATH环境变量
为了方便起见,还应该将GOPATH/bin
添加到PATH环境变量中。
要安装该库,请从命令行输入以下内容:
go get github.com/softlayer/softlayer-go/...
最后,您还应该具有阅读和编写Go代码的经验。
快速入门示例
让我们看一个使用softlayer-go库的简短示例:
package main
import (
"fmt"
"os"
"github.com/softlayer/softlayer-go/session"
"github.com/softlayer/softlayer-go/services"
)
const (
USERNAME = "your user name"
APIKEY = "your api key"
)
func main() {
sess := session.New(USERNAME, APIKEY)
accountService := services.GetAccountService(sess)
account, err := accountService.GetObject()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(*account.Id, *account.CompanyName)
}
总结以上代码中采取的步骤:
- 导入所需的softlayer-go软件包。
- 通过传递您的SoftLayer用户名和API密钥来创建新会话。
- 获取SoftLayer帐户服务的句柄。 这里列出了所有SoftLayer服务的
Get
方法。 方法名称始终以Get
为前缀,并以Service
作为后缀,如services.GetServiceNameHereService(...)
。 如API文档中所示,这将忽略出现在服务名称中的任何下划线。 - 在帐户服务上调用方法; 基本上,您正在获取帐户信息。 这可能会返回您要检查的错误。
- 打印出帐户ID和帐户名。
建立会议
让我们仔细看看创建新会话的那一行:
sess := session.New(USERNAME, APIKEY)
session.New()
可以按以下顺序获取(最多四个)字符串参数的变量列表:
- 用户名
- API密钥
- API端点
- HTTP超时(以秒为单位)
API端点和HTTP超时分别具有默认值https://api.softlayer.com/rest/v3
和120
。 您可以通过传递第三个和第四个参数以任何希望的方式覆盖它们。
会话值也可以从环境中读取。 从中读取的环境变量分别是:
-
SL_USERNAME
-
SL_API_KEY
-
SL_ENDPOINT_URL
-
SL_TIMEOUT
如果您在环境中设置了用户名和API密钥,则只需在代码中不带参数的情况下创建一个新会话即可:
sess := session.New()
此外,会话值也可以从本地配置文件中读取。 该库将在~/.softlayer
(在Windows上为%USERPROFILE%/.softlayer
)中查找文件。 格式应如下所示:
[softlayer]
username = <your username>
api_key = <your api key>
endpoint_url = <optional>
timeout = <optional>
将参数传递给New()
将始终覆盖环境或本地配置文件中的相应参数。 环境优先于本地配置文件。
既然有了会话参考,就可以获取任何SoftLayer服务的句柄。 这始终需要传递会话引用:
accountService := services.GetAccountService(sess)
实例化虚拟访客
让我们看另一个例子。 这次,在SoftLayer上创建了一个虚拟访客:
package main
import (
"fmt"
"os"
"github.com/softlayer/softlayer-go/datatypes"
"github.com/softlayer/softlayer-go/services"
"github.com/softlayer/softlayer-go/session"
"github.com/softlayer/softlayer-go/sl"
)
func main() {
sess := session.New()
service := services.GetVirtualGuestService(sess)
guestTpl := datatypes.Virtual_Guest{
Hostname: sl.String("sample"),
Domain: sl.String("example.com"),
MaxMemory: sl.Int(2048),
StartCpus: sl.Int(1),
Datacenter: &datatypes.Location{Name: sl.String("sjc01")},
OperatingSystemReferenceCode: sl.String("UBUNTU_LATEST"),
LocalDiskFlag: sl.Bool(true),
}
guest, err := service.Mask("id;domain").CreateObject(&guestTpl)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Printf("New Virtual Guest created with ID %d\n", *guest.Id)
fmt.Printf("Domain: %s\n", *guest.Domain)
}
要创建虚拟来宾实例,您需要使用允许您指定某些必需属性的虚拟来宾结构。 这成为您的访客模板。 如上所示初始化此模板后,您可以调用虚拟访客服务的CreateObject()
方法,以通过将模板传递给实例在云上创建实例。
请注意sl包中用于在结构中设置值的帮助程序方法。 这些用于提供指向文字值的指针。 另外,请注意使用链接方法Mask("id;domain")
,该方法用于在基础API调用上设置对象掩码。 在以下各节中,我将详细讨论辅助工具和口罩。
服务方式
如前所述,服务包在SoftLayer中为每个服务都有一个结构。 这些中的每一个都具有用于该服务的所有预期方法。
让我们看一下Virtual_Guest
服务中的方法签名之一:
func (r Virtual_Guest) GetCpuMetricDataByDate(
startDateTime *datatypes.Time,
endDateTime *datatypes.Time,
cpuIndexes []int) (resp []datatypes.Metric_Tracking_Object_Data, err error)
在softlayer-go中,除了slice参数外,每个方法参数都应是一个指针。 对于slice参数,您将直接传递该参数,因为slice是Go中的隐式指针。
每个方法还将返回一个错误。 所有错误都将包含sl.Error
(在以下各节中对此进行详细介绍)。 如果方法具有其他返回值,则它们也将与错误一起返回,如上例所示。
这就是调用上述方法的样子:
sess := session.New()
service := services.GetVirtualGuestService(sess)
start := time.Date(2010, 12, 31, 0, 0, 0, 0, time.FixedZone("Atlantic", -60*60*4))
end := time.Now()
resp, err := service.GetCpuMetricDataByDate(
&datatypes.Time{start}, &datatypes.Time{end},
[]int{1, 2, 3},
)
在处理时间值时,库会将它们作为自己的datatypes.Time
类型进行处理。 这不仅与时间有关的方法参数适用,而且与时间有关的任何数据类型字段也适用。 这允许库从/到SoftLayer API端点适当地对时间值进行解码/编码,并始终一致地对待该类型的值。
结果,当需要将时间值作为方法参数传递时,用户将不得不将它们包装在datatypes.Time
结构中。 请注意,我们传递了对这些结构的引用,而整数数组则按原样传递。
服务上下文修饰符
使用SoftLayer API时,您可以引用资源或对象的特定实例。 实际上,某些方法需要这样做。 例如,当请求特定用户的信息时,您必须指定该用户的ID。 但是,API不会将此ID作为方法参数来处理,因此库将其作为上下文参数来处理:
service := services.GetUserCustomerService(sess)
user, err := service.Id(6786566).GetObject()
每个服务都有一组函数,您可以使用这些函数传递这些上下文参数,例如您要获取其数据的资源的ID,如上所示。 这是这些功能类型的完整列表:
-
Id(id int)
-操作将作用于的资源的ID。 -
Limit(limit int)
-对于返回集合的方法,设置要返回的最大结果数。 -
Offset(offset int)
-对于返回集合的方法,设置在标记返回的集合的开始之前要跳过多少个结果。 -
Mask(id string)
从API提取对象时用于获取关系属性; 还可以让您指定您感兴趣的字段,以便API仅返回该数据。 -
Filter(filter string)
-对于返回集合的方法,用于限制返回的结果数。
以下代码段将返回带有单个公共图像的列表,因为您已在过滤器中明确指定了图像的全局标识符。 此外,API仅返回在对象掩码中设置的字段。
service := services.GetVirtualGuestBlockDeviceTemplateGroupService(sess)
publicImages, err := service.
Mask("id;name;note;summary").
Filter(`{"globalIdentifier":{"operation":"2e61f677-752b-4020-a447-b138f5daa387"}}`).GetPublicImages()
您在对象掩码和过滤器中使用的字段名称不大写。 它们必须与API数据类型文档相匹配。 但是,出于可见性原因,Go中相应的数据结构会将这些字段名称大写。 例如,使用*publicImages[0].Summary
访问在对象掩码中指定的公共图像摘要。 请参阅对象遮罩文档以了解更多信息。
上面,掩码和过滤器仅应用于该请求。 也就是说,如果您再次使用相同的服务引用,则掩码和过滤器将为空白。 上面列出的其他上下文参数也是如此。 如果您希望保留设置的掩码和过滤器以供将来的方法调用,请保存上下文参数函数返回的服务句柄:
serviceWithMaskAndFilter := service.
Mask("id;name;note;summary").
Filter(`{"globalIdentifier":{"operation":"2e61f677-752b-4020-a447-b138f5daa387"}}`)
publicImages, err := serviceWithMaskAndFilter.GetPublicImages()
对象过滤器
过滤器指定为字符串。 考虑到复杂的对象过滤器会变得多么复杂,该库提供了一个过滤器构建器,以帮助使此过程更加轻松。 例如,以下是使用过滤器生成器指定与上一片段相同的过滤器的方法:
service.Filter(
filter.Path("globalIdentifier").
Eq("2e61f677-752b-4020-a447-b138f5daa387").Build(),
).GetPublicImages()
也可以预先创建,批量创建过滤器,并使用其他条件创建过滤器:
filters := filter.New(
filter.Path("virtualGuests.hostname").StartsWith("demo"),
filter.Path("virtualGuests.id").NotEq(1234),
)
稍后,您可以在使用该组之前将另一个过滤器附加到该组:
filters = append(filters, filter.Path("virtualGuests.domain").EndsWith("example.com"))
guests, err := accountService.Filter(filters.Build()).GetVirtualGuests()
filter.Path()
可用于在SoftLayer中指定对象的嵌套关系属性的条件。 例如:
filter.Path("datacenter.locationStatus.status").Eq("ACTIVE")
sl软件包中的便利功能
与方法参数一样,每个数据类型结构的每个字段都需要一个指针(那些是切片的字段除外)。 如果值是nil,则允许库在将数据类型结构中未使用的字段发布到API时忽略它们,因为Go中只能使用指针(和interface{}
类型)。
该库具有一个帮助程序包,可帮助您从SoftLayer结构中获取和设置值时进行处理。 再看一下前面的例子之一:
guestTpl := datatypes.Virtual_Guest{
Hostname: sl.String("sample"),
Domain: sl.String("example.com"),
MaxMemory: sl.Int(2048),
StartCpus: sl.Int(1),
Datacenter: &datatypes.Location{Name: sl.String("sjc01")},
OperatingSystemReferenceCode: sl.String("UBUNTU_LATEST"),
LocalDiskFlag: sl.Bool(true),
}
sl.
软件包具有本机类型的指针帮助器,例如字符串,整数,无符号整数和布尔值。 它们仅返回指向您传递的值的指针引用。 没有帮助者,您将不得不像这样重写上面的代码:
hostname := "sample"
domain := "example.com"
maxMemory := 2048
startCpus := 1
datacenterName := "sjc01"
osName := "UBUNTU_LATEST"
localDiskFlag := true
guestTpl := datatypes.Virtual_Guest{
Hostname: &hostname,
Domain: &domain,
MaxMemory: &maxMemory,
StartCpus: &startCpus,
Datacenter: &datatypes.Location{Name: &datacenterName},
OperatingSystemReferenceCode: &osName,
LocalDiskFlag: &localDiskFlag,
}
在某些情况下,这可能很方便,尤其是当您希望始终将这些值用作常量时。 在其他情况下,它是不必要的冗长。 尽可能使用助手来使您的代码更简洁。
当您需要在结构中设置值时,这些帮助程序很有用,但是也有一些帮助程序可以从SoftLayer结构中获取值。 例如,考虑以下代码片段,您需要在其中获取虚拟访客的安装后脚本URI:
// Don't assume the post install script uri is non-nil.
var scriptURI string
if guest.PostInstallScriptUri != nil {
scriptURI = *guest.PostInstallScriptUri
}
使用sl.Get
帮助器,可以使用以下缩写(在Go类型断言的帮助下):
scriptURI := sl.Get(guest.PostInstallScriptUri).(string)
sl
包获取器帮助器是:
-
Get(p interface{}, d ...interface{}) interface{}
返回p
的值。 如果p
是一个指针,它将为您取消引用并返回该值。 您还可以传入一个可选的默认值作为第二个参数。 如果p
为nil,则返回d
。 否则,如果p
为nil且没有默认值,则将获得p
的零值(意味着,如果p
是字符串或指向字符串的指针,则将获得空字符串。如果它是整数或指向整数的指针,则将得到零,依此类推)。 -
GetOk(p interface{}) (interface{}, bool)
与Get()
几乎相同,但是它将返回一个附加的布尔值,指示p
是否为非nil指针。 此处不需要默认值选项,因为在没有值的情况下,要有自己的逻辑,这可以包括确定适当的默认值。
有时候,您需要从SoftLayer结构中获取深层嵌套的值。 由于字段可以是指针,因此避免意外的Go恐慌的最安全方法是在每个结构级别测试nil:
var vlanId int
if guest.PrimaryNetworkComponent != nil {
if guest.PrimaryNetworkComponent.NetworkVlan != nil {
if guest.PrimaryNetworkComponent.NetworkVlan.Id != nil {
vlanId = *guest.PrimaryNetworkComponent.NetworkVlan.Id
}
}
}
可以使用另一个帮助程序,该帮助程序可以帮助从诸如该位置的嵌套位置获取值,并将消除很多样板代码:
vlanId := sl.Grab(guest, "PrimaryNetworkComponent.NetworkVlan.Id").(int)
这是sl
软件包grab
助手:
-
Grab(s interface{}, path string, d ...interface{}) interface{}
返回以s
为起点的给定路径指定的值。path
是一个点分隔的字段集,这些字段从s
遍历以获取要获取的值。 如果在路径遍历的任何时候都获得nil指针值,则将返回类型适当的零值。 -
GrabOk(s interface{}, path string) (interface{}, bool)
与Grab()
相似,不同之处在于它返回另一个布尔值,该值指示是否成功找到了一个值(在这种情况下,它必须创建一个零)值)。
当没有可用值并且您想以特殊方式处理这种情况时, GrabOk
可能会很有用:
if vlanId, ok := sl.GrabOk(guest, "PrimaryNetworkComponent.NetworkVlan.Id").(int); !ok {
log.Println("No VLAN ID found!")
}
处理错误
所有服务方法都可以返回错误。 除了实现Go's Error
接口之外,下面显示的Error
是一种SoftLayer类型,您可以检查该类型以获得有关此Error
更多信息。
type Error struct {
StatusCode int
Exception string
Message string
Wrapped error
}
例如,如果您想要一种精确的方法来确定错误是由于资源不存在还是某些其他传输或API错误所致,这将很有用。
_, err := service.Id(0). // invalid object ID
GetObject()
if err != nil {
// Note: type assertion is only necessary for inspecting individual fields
apiErr, ok := err.(sl.Error)
if apiError.StatusCode == 404 {
// handle not found error here
}
// more generic error handling here
}
如果您需要进一步展开,总是可以在.Wrapped
找到原始错误。
if err != nil {
// Note: type assertion is only necessary for inspecting individual fields
apiErr, ok := err.(sl.Error)
if apiError.StatusCode == 404 {
// handle not found error here
} else if netError, ok := apiError.Wrapped.(net.Error); ok && netError.Timeout() {
// handle transport timeout error here
}
// more generic error handling here
}
XML-RPC和基于密码的身份验证
SoftLayer API支持REST和XML-RPC端点。 两者之间的功能几乎相同,除了我所知的一个领域仅适用于XML-RPC,特别是当您需要使用密码而不是API密钥对API进行身份验证时。
为此,您需要在会话中指定XML-RPC端点并进行密码验证,如下所示:
func main() {
// Create a session specifying an XML-RPC endpoint url.
sess := &session.Session{
Endpoint: "https://api.softlayer.com/xmlrpc/v3",
}
// Get a token from the api using your username and password
userService := services.GetUserCustomerService(sess)
token, err := userService.GetPortalLoginToken(USERNAME, PASSWORD, nil, nil)
if err != nil {
log.Fatal(err)
}
// Add user id and token to the session.
sess.UserId = *token.UserId
sess.AuthToken = *token.Hash
// You have a complete authenticated session now.
// Call any api from this point on as normal...
keys, err := userService.Id(sess.UserId).GetApiAuthenticationKeys()
if err != nil {
log.Fatal(err)
}
log.Println("API Key:", *keys[0].AuthenticationKey)
}
调试
要查看API请求和响应的日志,只需打开会话调试标志,如下所示:
sess := session.New()
sess.Debug = true
// use the session reference throughout
accountService := services.GetAccountService(sess)
// ...
在尝试使用该库和SoftLayer API时,这可能非常有用。
结论
现在,您已经学习了如何从Go编程语言开始控制SoftLayer API。 您已经研究了会话初始化,获取服务句柄,方法调用,数据类型结构以及如何更好地初始化和导航它们,如何实例化虚拟服务器,设置对象掩码,使用过滤器生成器,处理错误以及其他一些事情。 做得好!
当然,这只是您使用SoftLayer(功能强大且功能丰富的云平台)所能做的事情的起点。 我鼓励您按照下面“相关主题”部分中的链接查找更高级的示例。
致谢:非常感谢Michael Rieth提出了库的第一版和说明。
翻译自: https://www.ibm.com/developerworks/cloud/library/cl-softlayer-go-overview/index.html