Add external ID to host output (#1705)

This also fixes a bug if no hosts were returned where the job would run
over and over in a loop, and some other cleanup.
This commit is contained in:
Jeff Mitchell authored and GitHub committed 2021-11-13 17:18:20 -05:00
1 parent 4bf86e6394
commit b3fd23863f
13 files changed
+147 -54

No files matched your search

+1
View File
@@ -28,6 +28,7 @@ type Host struct {
Attributes map[string]interface{} `json:"attributes,omitempty"`
IpAddresses []string `json:"ip_addresses,omitempty"`
DnsNames []string `json:"dns_names,omitempty"`
ExternalId string `json:"external_id,omitempty"`
AuthorizedActions []string `json:"authorized_actions,omitempty"`
response *api.Response
+1
View File
@@ -65,6 +65,7 @@ const (
PluginNameField = "plugin_name"
IpAddressesField = "ip_addresses"
DnsNamesField = "dns_names"
ExternalIdField = "external_id"
EgressCredentialSourceIdsField = "egress_credential_source_ids"
EgressCredentialSourcesField = "egress_credential_sources"
)
+8
View File
@@ -77,6 +77,11 @@ func (c *Command) printListTable(items []*hosts.Host) string {
fmt.Sprintf(" ID: %s", "(not available)"),
)
}
if item.ExternalId != "" {
output = append(output,
fmt.Sprintf(" External ID: %s", item.ExternalId),
)
}
if item.Version > 0 {
output = append(output,
fmt.Sprintf(" Version: %d", item.Version),
@@ -135,6 +140,9 @@ func printItemTable(result api.GenericResult) string {
if item.HostCatalogId != "" {
nonAttributeMap["Host Catalog ID"] = item.HostCatalogId
}
if item.ExternalId != "" {
nonAttributeMap["External ID"] = item.ExternalId
}
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
+5
View File
@@ -4247,6 +4247,11 @@
"description": "Output only. The list of dns addresses associated with this host.",
"readOnly": true
},
"external_id": {
"type": "string",
"description": "Output only. The external ID of the host, if any.",
"readOnly": true
},
"authorized_actions": {
"type": "array",
"items": {
+4 -2
View File
@@ -299,8 +299,10 @@ func (r *SetSyncJob) syncSets(ctx context.Context, setIds []string) error {
return errors.Wrap(ctx, err, op)
}
if _, err := r.upsertHosts(ctx, ci.storeCat, catSetIds, resp.GetHosts()); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts"))
if len(resp.GetHosts()) > 0 {
if _, err := r.upsertHosts(ctx, ci.storeCat, catSetIds, resp.GetHosts()); err != nil {
return errors.Wrap(ctx, err, op, errors.WithMsg("upserting hosts"))
}
}
updateSyncDataQuery := `
+1 -1
View File
@@ -170,7 +170,7 @@ func TestSetSyncJob_Run(t *testing.T) {
{
ExternalId: "first",
IpAddresses: []string{"10.0.0.1"},
DnsNames: []string{"foo.com"},
DnsNames: []string{"foo.com"},
SetIds: setIds,
},
},
+52 -12
View File
@@ -37,8 +37,9 @@ func NewLoopbackPlugin() plgpb.HostPluginServiceServer {
hostMap: make(map[string][]*loopbackPluginHostInfo),
}
ret.OnCreateCatalogFn = ret.onCreateCatalog
ret.OnCreateSetFn = ret.onCreateSet
ret.OnUpdateCatalogFn = ret.onUpdateCatalog
ret.OnCreateSetFn = ret.onCreateSet
ret.OnUpdateSetFn = ret.onUpdateSet
ret.OnDeleteSetFn = ret.onDeleteSet
ret.ListHostsFn = ret.listHosts
return ret
@@ -61,6 +62,23 @@ func (l *loopbackPlugin) onCreateCatalog(ctx context.Context, req *plgpb.OnCreat
return &plgpb.OnCreateCatalogResponse{}, nil
}
func (l *loopbackPlugin) onUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) {
const op = "plugin.(loopbackPlugin).onUpdateCatalog"
if req == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
}
if cat := req.GetNewCatalog(); cat != nil {
if secrets := cat.GetSecrets(); secrets != nil {
return &plgpb.OnUpdateCatalogResponse{
Persisted: &plgpb.HostCatalogPersisted{
Secrets: secrets,
},
}, nil
}
}
return &plgpb.OnUpdateCatalogResponse{}, nil
}
func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSetRequest) (*plgpb.OnCreateSetResponse, error) {
const op = "plugin.(loopbackPlugin).onCreateSet"
if req == nil {
@@ -93,24 +111,46 @@ func (l *loopbackPlugin) onCreateSet(ctx context.Context, req *plgpb.OnCreateSet
}
}
}
return nil, nil
return &plgpb.OnCreateSetResponse{}, nil
}
func (l *loopbackPlugin) onUpdateCatalog(ctx context.Context, req *plgpb.OnUpdateCatalogRequest) (*plgpb.OnUpdateCatalogResponse, error) {
const op = "plugin.(loopbackPlugin).onUpdateCatalog"
func (l *loopbackPlugin) onUpdateSet(ctx context.Context, req *plgpb.OnUpdateSetRequest) (*plgpb.OnUpdateSetResponse, error) {
const op = "plugin.(loopbackPlugin).onCreateSet"
if req == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "req is nil")
}
if cat := req.GetNewCatalog(); cat != nil {
if secrets := cat.GetSecrets(); secrets != nil {
return &plgpb.OnUpdateCatalogResponse{
Persisted: &plgpb.HostCatalogPersisted{
Secrets: secrets,
},
}, nil
set := req.GetNewSet()
if set == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "set is nil")
}
var hosts []*loopbackPluginHostInfo
if attrs := set.GetAttributes(); attrs != nil {
attrsMap := attrs.AsMap()
if field := attrsMap[loopbackPluginHostInfoAttrField]; field != nil {
switch t := field.(type) {
case []interface{}:
for _, h := range t {
hostInfo := new(loopbackPluginHostInfo)
if err := mapstructure.Decode(h, hostInfo); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
hosts = append(hosts, hostInfo)
}
case map[string]interface{}:
hostInfo := new(loopbackPluginHostInfo)
if err := mapstructure.Decode(t, hostInfo); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
hosts = append(hosts, hostInfo)
default:
return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown host info type %T", t))
}
}
}
return &plgpb.OnUpdateCatalogResponse{}, nil
if hosts != nil {
l.hostMap[set.GetId()] = hosts
}
return &plgpb.OnUpdateSetResponse{}, nil
}
func (l *loopbackPlugin) onDeleteSet(ctx context.Context, req *plgpb.OnDeleteSetRequest) (*plgpb.OnDeleteSetResponse, error) {
@@ -56,6 +56,9 @@ message Host {
// Output only. The list of dns addresses associated with this host.
repeated string dns_names = 130;
// Output only. The external ID of the host, if any.
string external_id = 140;
// Output only. The available actions on this resource for this user.
repeated string authorized_actions = 300 [json_name="authorized_actions"];
}
@@ -131,12 +131,11 @@ func (s Service) ListHosts(ctx context.Context, req *pbs.ListHostsRequest) (*pbs
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
}
var hostSetIds []string
switch h := item.(type) {
case *plugin.Host:
hostSetIds = h.SetIds
outputOpts = append(outputOpts, handlers.WithHostSetIds(h.SetIds))
}
item, err := toProto(ctx, item, hostSetIds, outputOpts...)
item, err := toProto(ctx, item, outputOpts...)
if err != nil {
return nil, err
}
@@ -182,12 +181,11 @@ func (s Service) GetHost(ctx context.Context, req *pbs.GetHostRequest) (*pbs.Get
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings()))
}
var hostSetIds []string
switch h := h.(type) {
case *plugin.Host:
hostSetIds = h.SetIds
outputOpts = append(outputOpts, handlers.WithHostSetIds(h.SetIds))
}
item, err := toProto(ctx, h, hostSetIds, outputOpts...)
item, err := toProto(ctx, h, outputOpts...)
if err != nil {
return nil, err
}
@@ -226,7 +224,7 @@ func (s Service) CreateHost(ctx context.Context, req *pbs.CreateHostRequest) (*p
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings()))
}
item, err := toProto(ctx, h, nil, outputOpts...)
item, err := toProto(ctx, h, outputOpts...)
if err != nil {
return nil, err
}
@@ -268,7 +266,7 @@ func (s Service) UpdateHost(ctx context.Context, req *pbs.UpdateHostRequest) (*p
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authResults.FetchActionSetForId(ctx, h.GetPublicId(), idActions).Strings()))
}
item, err := toProto(ctx, h, nil, outputOpts...)
item, err := toProto(ctx, h, outputOpts...)
if err != nil {
return nil, err
}
@@ -530,7 +528,7 @@ func toPluginInfo(plg *hostplugin.Plugin) *plugins.PluginInfo {
}
}
func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...handlers.Option) (*pb.Host, error) {
func toProto(ctx context.Context, in host.Host, opt ...handlers.Option) (*pb.Host, error) {
opts := handlers.GetOpts(opt...)
if opts.WithOutputFields == nil {
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "output fields not found when building host proto")
@@ -565,12 +563,7 @@ func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...hand
out.UpdatedTime = in.GetUpdateTime().GetTimestamp()
}
if outputFields.Has(globals.VersionField) {
switch in.(type) {
case *static.Host:
out.Version = in.GetVersion()
case *plugin.Host:
out.Version = 1
}
out.Version = in.GetVersion()
}
if outputFields.Has(globals.ScopeField) {
out.Scope = opts.WithScope
@@ -578,10 +571,8 @@ func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...hand
if outputFields.Has(globals.AuthorizedActionsField) {
out.AuthorizedActions = opts.WithAuthorizedActions
}
if outputFields.Has(globals.HostSetIdsField) {
for _, hs := range hostSetIds {
out.HostSetIds = append(out.HostSetIds, hs)
}
if outputFields.Has(globals.HostSetIdsField) && len(opts.WithHostSetIds) > 0 {
out.HostSetIds = opts.WithHostSetIds
}
if outputFields.Has(globals.AttributesField) {
switch h := in.(type) {
@@ -604,6 +595,13 @@ func toProto(ctx context.Context, in host.Host, hostSetIds []string, opt ...hand
if outputFields.Has(globals.DnsNamesField) {
out.DnsNames = h.DnsNames
}
if outputFields.Has(globals.ExternalIdField) {
out.ExternalId = h.ExternalId
}
// FIXME? Seems we should be _using_ a version...
if outputFields.Has(globals.VersionField) {
out.Version = 1
}
}
return &out, nil
}
@@ -174,6 +174,7 @@ func TestGet_Plugin(t *testing.T) {
Description: plg.GetDescription(),
},
HostSetIds: []string{hs.GetPublicId()},
ExternalId: "test",
AuthorizedActions: testAuthorizedActions[plugin.Subtype],
}
@@ -359,7 +360,8 @@ func TestList_Plugin(t *testing.T) {
var wantHs []*pb.Host
for i := 0; i < 10; i++ {
h := plugin.TestHost(t, conn, hc.GetPublicId(), fmt.Sprintf("host %d", i))
extId := fmt.Sprintf("host %d", i)
h := plugin.TestHost(t, conn, hc.GetPublicId(), extId)
plugin.TestSetMembers(t, conn, hs.GetPublicId(), []*plugin.Host{h})
wantHs = append(wantHs, &pb.Host{
Id: h.GetPublicId(),
@@ -374,6 +376,7 @@ func TestList_Plugin(t *testing.T) {
UpdatedTime: h.GetUpdateTime().GetTimestamp(),
HostSetIds: []string{hs.GetPublicId()},
Version: 1,
ExternalId: extId,
Type: plugin.Subtype.String(),
AuthorizedActions: testAuthorizedActions[plugin.Subtype],
})
@@ -74,4 +74,16 @@ func Test_GetOpts(t *testing.T) {
opts = GetOpts(WithMemberIds(out))
require.Equal(out, opts.WithMemberIds)
})
t.Run("WithHostSetIds", func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
opts := GetOpts()
assert.Nil(opts.WithHostSetIds)
out := []string{"foobar"}
opts = GetOpts(WithHostSetIds(out))
require.Equal(out, opts.WithHostSetIds)
})
}
@@ -32,6 +32,7 @@ type options struct {
WithAuthorizedCollectionActions map[string]*structpb.ListValue
WithManagedGroupIds []string
WithMemberIds []string
WithHostSetIds []string
}
func getDefaultOptions() options {
@@ -110,3 +111,11 @@ func WithMemberIds(ids []string) Option {
o.WithMemberIds = ids
}
}
// WithHostSetIds provides an option when creating responses to include the
// given host set IDs if allowed
func WithHostSetIds(ids []string) Option {
return func(o *options) {
o.WithHostSetIds = ids
}
}
@@ -61,6 +61,8 @@ type Host struct {
IpAddresses []string `protobuf:"bytes,120,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"`
// Output only. The list of dns addresses associated with this host.
DnsNames []string `protobuf:"bytes,130,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"`
// Output only. The external ID of the host, if any.
ExternalId string `protobuf:"bytes,140,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"`
// Output only. The available actions on this resource for this user.
AuthorizedActions []string `protobuf:"bytes,300,rep,name=authorized_actions,proto3" json:"authorized_actions,omitempty"`
}
@@ -195,6 +197,13 @@ func (x *Host) GetDnsNames() []string {
return nil
}
func (x *Host) GetExternalId() string {
if x != nil {
return x.ExternalId
}
return ""
}
func (x *Host) GetAuthorizedActions() []string {
if x != nil {
return x.AuthorizedActions
@@ -272,8 +281,8 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{
0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63,
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31,
0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd,
0x05, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20,
0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9f,
0x06, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f,
0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x5f, 0x69,
@@ -317,23 +326,25 @@ var file_controller_api_resources_hosts_v1_host_proto_rawDesc = []byte{
0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x78, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0b, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12,
0x1c, 0x0a, 0x09, 0x64, 0x6e, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x82, 0x01, 0x20,
0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2f, 0x0a,
0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x75,
0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29, 0x1d, 0x0a,
0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f,
0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73, 0x2f, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b, 0x68, 0x6f,
0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x03, 0x28, 0x09, 0x52, 0x08, 0x64, 0x6e, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x20, 0x0a,
0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x8c, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x12,
0x2f, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xac, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x22, 0x75, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x41, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5d, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x25, 0xa0, 0xda, 0x29, 0x01, 0xc2, 0xdd, 0x29,
0x1d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07,
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x62, 0x73,
0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x3b,
0x68, 0x6f, 0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (