<commit_before>package flag

type AppName struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
}

type OptionalAppName struct {
	AppName string `positional-arg-name:"APP_NAME" description:"The application name"`
}

type Buildpack struct {
	Buildpack string `positional-arg-name:"BUILDPACK" required:"true" description:"The buildpack"`
}

type CommandName struct {
	CommandName string `positional-arg-name:"COMMAND_NAME" description:"The command name"`
}

type Domain struct {
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type Feature struct {
	Feature string `positional-arg-name:"FEATURE_NAME" required:"true" description:"The feature flag name"`
}

type ParamsAsJSON struct {
	JSON string `positional-arg-name:"JSON" required:"true" description:"Parameters as JSON"`
}

type Service struct {
	Service string `positional-arg-name:"SERVICE" required:"true" description:"The service offering name"`
}

type ServiceInstance struct {
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance name"`
}

type Organization struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
}

type APIPath struct {
	Path string `positional-arg-name:"PATH" required:"true" description:"The API endpoint"`
}

type PluginRepoName struct {
	PluginRepoName string `positional-arg-name:"REPO_NAME" required:"true" description:"The plugin repo name"`
}

type PluginName struct {
	PluginName string `positional-arg-name:"PLUGIN_NAME" required:"true" description:"The plugin name"`
}

type Quota struct {
	Quota string `positional-arg-name:"QUOTA" required:"true" description:"The organization quota"`
}

type SecurityGroup struct {
	ServiceGroup string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group"`
}

type ServiceBroker struct {
	ServiceBroker string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The service broker"`
}

type Space struct {
	Space string `positional-arg-name:"SPACE" required:"true" description:"The space"`
}

type SpaceQuota struct {
	SpaceQuota string `positional-arg-name:"SPACE_QUOTA_NAME" required:"true" description:"The space quota"`
}

type StackName struct {
	StackName string `positional-arg-name:"STACK_NAME" required:"true" description:"The stack name"`
}

type Username struct {
	Username string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
}

type APITarget struct {
	URL string `positional-arg-name:"URL" description:"API URL to target"`
}

type Authentication struct {
	Username string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password string `positional-arg-name:"PASSWORD" required:"true" description:"The password"`
}

type CreateUser struct {
	Username string  `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password *string `positional-arg-name:"PASSWORD" description:"The password"`
}

type AppInstance struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Index   int    `positional-arg-name:"INDEX" required:"true" description:"The index of the application instance"`
}

type OrgSpace struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Space        string `positional-arg-name:"SPACE" required:"true" description:"The space"`
}

type ServiceInstanceKey struct {
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
	ServiceKey      string `positional-arg-name:"SERVICE_KEY" required:"true" description:"The service key"`
}

type AppDomain struct {
	App    string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type HostDomain struct {
	Host   string `positional-arg-name:"HOST" required:"true" description:"The hostname"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type OrgDomain struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Domain       string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type SpaceDomain struct {
	Space  string `positional-arg-name:"SPACE" required:"true" description:"The space"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type BindSecurityGroupArgs struct {
	SecurityGroupName string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group name"`
	OrganizationName  string `positional-arg-name:"ORG" required:"true" description:"The organization group name"`
	SpaceName         string `positional-arg-name:"SPACE" description:"The space name"`
}

type UnbindSecurityGroupArgs struct {
	SecurityGroupName string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group name"`
	OrganizationName  string `positional-arg-name:"ORG" description:"The organization group name"`
	SpaceName         string `positional-arg-name:"SPACE" description:"The space name"`
}

type FilesArgs struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Path    string `positional-arg-name:"PATH" description:"The file path"`
}

type SetEnvironmentArgs struct {
	AppName                  string              `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	EnvironmentVariableName  string              `positional-arg-name:"ENV_VAR_NAME" required:"true" description:"The environment variable name"`
	EnvironmentVariableValue EnvironmentVariable `positional-arg-name:"ENV_VAR_VALUE" required:"true" description:"The environment variable value"`
}

type UnsetEnvironmentArgs struct {
	AppName                 string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	EnvironmentVariableName string `positional-arg-name:"ENV_VAR_NAME" required:"true" description:"The environment variable name"`
}

type CopySourceArgs struct {
	SourceAppName string `positional-arg-name:"SOURCE-APP" required:"true" description:"The old application name"`
	TargetAppName string `positional-arg-name:"TARGET-NAME" required:"true" description:"The new application name"`
}

type CreateServiceArgs struct {
	ServiceOffering string `positional-arg-name:"SERVICE" required:"true" description:"The service offering"`
	ServicePlan     string `positional-arg-name:"SERVICE_PLAN" required:"true" description:"The service plan that the service instance will use"`
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type RenameServiceArgs struct {
	ServiceInstance        string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance to rename"`
	NewServiceInstanceName string `positional-arg-name:"NEW_SERVICE_INSTANCE" required:"true" description:"The new name of the service instance"`
}

type BindServiceArgs struct {
	AppName             string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	ServiceInstanceName string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type RouteServiceArgs struct {
	Domain          string `positional-arg-name:"DOMAIN" required:"true" description:"The domain of the route"`
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type AppRenameArgs struct {
	OldAppName string `positional-arg-name:"APP_NAME" required:"true" description:"The old application name"`
	NewAppName string `positional-arg-name:"NEW_APP_NAME" required:"true" description:"The new application name"`
}

type RenameOrgArgs struct {
	OldOrgName string `positional-arg-name:"ORG" required:"true" description:"The old organization name"`
	NewOrgName string `positional-arg-name:"NEW_ORG" required:"true" description:"The new organization name"`
}

type RenameSpaceArgs struct {
	OldSpaceName string `positional-arg-name:"SPACE_NAME" required:"true" description:"The old space name"`
	NewSpaceName string `positional-arg-name:"NEW_SPACE_NAME" required:"true" description:"The new space name"`
}

type SetOrgQuotaArgs struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Quota        string `positional-arg-name:"QUOTA" required:"true" description:"The quota"`
}

type SetSpaceQuotaArgs struct {
	Space      string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space"`
	SpaceQuota string `positional-arg-name:"SPACE_QUOTA" required:"true" description:"The space quota"`
}

type SetHealthCheckArgs struct {
	AppName     string          `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	HealthCheck HealthCheckType `positional-arg-name:"HEALTH_CHECK_TYPE" required:"true" description:"Set to 'port' or 'none'"`
}

type CreateBuildpackArgs struct {
	Buildpack string                      `positional-arg-name:"BUILDPACK" required:"true" description:"The buildpack"`
	Path      PathWithExistenceCheckOrURL `positional-arg-name:"PATH" required:"true" description:"The path to the buildpack file"`
	Position  string                      `positional-arg-name:"POSITION" required:"true" description:"The position that sets priority"`
}

type RenameBuildpackArgs struct {
	OldBuildpackName string `positional-arg-name:"BUILDPACK_NAME" required:"true" description:"The old buildpack name"`
	NewBuildpackName string `positional-arg-name:"NEW_BUILDPACK_NAME" required:"true" description:"The new buildpack name"`
}

type SetOrgRoleArgs struct {
	Username     string  `positional-arg-name:"USERNAME" required:"true" description:"The user"`
	Organization string  `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Role         OrgRole `positional-arg-name:"ROLE" required:"true" description:"The organization role"`
}

type SetSpaceRoleArgs struct {
	Username     string    `positional-arg-name:"USERNAME" required:"true" description:"The user"`
	Organization string    `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Space        string    `positional-arg-name:"ORG" required:"true" description:"The space"`
	Role         SpaceRole `positional-arg-name:"ROLE" required:"true" description:"The space role"`
}

type ServiceAuthTokenArgs struct {
	Label    string `positional-arg-name:"LABEL" required:"true" description:"The token label"`
	Provider string `positional-arg-name:"PROVIDER" required:"true" description:"The token provider"`
	Token    string `positional-arg-name:"TOKEN" required:"true" description:"The token"`
}

type DeleteServiceAuthTokenArgs struct {
	Label    string `positional-arg-name:"LABEL" required:"true" description:"The token label"`
	Provider string `positional-arg-name:"PROVIDER" required:"true" description:"The token provider"`
}

type ServiceBrokerArgs struct {
	ServiceBroker string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The service broker name"`
	Username      string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password      string `positional-arg-name:"PASSWORD" required:"true" description:"The password"`
	URL           string `positional-arg-name:"URL" required:"true" description:"The URL of the service broker"`
}

type RenameServiceBrokerArgs struct {
	OldServiceBrokerName string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The old service broker name"`
	NewServiceBrokerName string `positional-arg-name:"NEW_SERVICE_BROKER" required:"true" description:"The new service broker name"`
}

type MigrateServiceInstancesArgs struct {
	V1Service  string `positional-arg-name:"v1_SERVICE" required:"true" description:"The old service offering"`
	V1Provider string `positional-arg-name:"v1_PROVIDER" required:"true" description:"The old service provider"`
	V1Plan     string `positional-arg-name:"v1_PLAN" required:"true" description:"The old service plan"`
	V2Service  string `positional-arg-name:"v2_SERVICE" required:"true" description:"The new service offering"`
	V2Plan     string `positional-arg-name:"v2_PLAN" required:"true" description:"The new service plan"`
}

type SecurityGroupArgs struct {
	SecurityGroup   string                 `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group"`
	PathToJsonRules PathWithExistenceCheck `positional-arg-name:"PATH_TO_JSON_RULES_FILE" required:"true" description:"Path to file of JSON describing security group rules"`
}

type AddPluginRepoArgs struct {
	PluginRepoName string `positional-arg-name:"REPO_NAME" required:"true" description:"The plugin repo name"`
	PluginRepoURL  string `positional-arg-name:"URL" required:"true" description:"The URL to the plugin repo"`
}

type InstallPluginArgs struct {
	PluginNameOrLocation Path `positional-arg-name:"PLUGIN_NAME_OR_LOCATION" required:"true" description:"The local path to the plugin, if the plugin exists locally; the URL to the plugin, if the plugin exists online; or the plugin name, if a repo is specified"`
}

type RunTaskArgs struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Command string `positional-arg-name:"COMMAND" required:"true" description:"The command to execute"`
}

type TerminateTaskArgs struct {
	AppName    string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	SequenceID string `positional-arg-name:"TASK_ID" required:"true" description:"The task's unique sequence ID"`
}

type IsolationSegmentName struct {
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type OrgIsolationArgs struct {
	OrganizationName     string `positional-arg-name:"ORG_NAME" required:"true" description:"The organization name"`
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type SpaceIsolationArgs struct {
	SpaceName            string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space name"`
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type ResetSpaceIsolationArgs struct {
	SpaceName string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space name"`
}

type ResetOrgDefaultIsolationArgs struct {
	OrgName string `positional-arg-name:"ORG_NAME" required:"true" description:"The organization name"`
}
<commit_msg>fixed set-space-role usage<commit_after>package flag

type AppName struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
}

type OptionalAppName struct {
	AppName string `positional-arg-name:"APP_NAME" description:"The application name"`
}

type Buildpack struct {
	Buildpack string `positional-arg-name:"BUILDPACK" required:"true" description:"The buildpack"`
}

type CommandName struct {
	CommandName string `positional-arg-name:"COMMAND_NAME" description:"The command name"`
}

type Domain struct {
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type Feature struct {
	Feature string `positional-arg-name:"FEATURE_NAME" required:"true" description:"The feature flag name"`
}

type ParamsAsJSON struct {
	JSON string `positional-arg-name:"JSON" required:"true" description:"Parameters as JSON"`
}

type Service struct {
	Service string `positional-arg-name:"SERVICE" required:"true" description:"The service offering name"`
}

type ServiceInstance struct {
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance name"`
}

type Organization struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
}

type APIPath struct {
	Path string `positional-arg-name:"PATH" required:"true" description:"The API endpoint"`
}

type PluginRepoName struct {
	PluginRepoName string `positional-arg-name:"REPO_NAME" required:"true" description:"The plugin repo name"`
}

type PluginName struct {
	PluginName string `positional-arg-name:"PLUGIN_NAME" required:"true" description:"The plugin name"`
}

type Quota struct {
	Quota string `positional-arg-name:"QUOTA" required:"true" description:"The organization quota"`
}

type SecurityGroup struct {
	ServiceGroup string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group"`
}

type ServiceBroker struct {
	ServiceBroker string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The service broker"`
}

type Space struct {
	Space string `positional-arg-name:"SPACE" required:"true" description:"The space"`
}

type SpaceQuota struct {
	SpaceQuota string `positional-arg-name:"SPACE_QUOTA_NAME" required:"true" description:"The space quota"`
}

type StackName struct {
	StackName string `positional-arg-name:"STACK_NAME" required:"true" description:"The stack name"`
}

type Username struct {
	Username string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
}

type APITarget struct {
	URL string `positional-arg-name:"URL" description:"API URL to target"`
}

type Authentication struct {
	Username string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password string `positional-arg-name:"PASSWORD" required:"true" description:"The password"`
}

type CreateUser struct {
	Username string  `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password *string `positional-arg-name:"PASSWORD" description:"The password"`
}

type AppInstance struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Index   int    `positional-arg-name:"INDEX" required:"true" description:"The index of the application instance"`
}

type OrgSpace struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Space        string `positional-arg-name:"SPACE" required:"true" description:"The space"`
}

type ServiceInstanceKey struct {
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
	ServiceKey      string `positional-arg-name:"SERVICE_KEY" required:"true" description:"The service key"`
}

type AppDomain struct {
	App    string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type HostDomain struct {
	Host   string `positional-arg-name:"HOST" required:"true" description:"The hostname"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type OrgDomain struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Domain       string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type SpaceDomain struct {
	Space  string `positional-arg-name:"SPACE" required:"true" description:"The space"`
	Domain string `positional-arg-name:"DOMAIN" required:"true" description:"The domain"`
}

type BindSecurityGroupArgs struct {
	SecurityGroupName string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group name"`
	OrganizationName  string `positional-arg-name:"ORG" required:"true" description:"The organization group name"`
	SpaceName         string `positional-arg-name:"SPACE" description:"The space name"`
}

type UnbindSecurityGroupArgs struct {
	SecurityGroupName string `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group name"`
	OrganizationName  string `positional-arg-name:"ORG" description:"The organization group name"`
	SpaceName         string `positional-arg-name:"SPACE" description:"The space name"`
}

type FilesArgs struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Path    string `positional-arg-name:"PATH" description:"The file path"`
}

type SetEnvironmentArgs struct {
	AppName                  string              `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	EnvironmentVariableName  string              `positional-arg-name:"ENV_VAR_NAME" required:"true" description:"The environment variable name"`
	EnvironmentVariableValue EnvironmentVariable `positional-arg-name:"ENV_VAR_VALUE" required:"true" description:"The environment variable value"`
}

type UnsetEnvironmentArgs struct {
	AppName                 string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	EnvironmentVariableName string `positional-arg-name:"ENV_VAR_NAME" required:"true" description:"The environment variable name"`
}

type CopySourceArgs struct {
	SourceAppName string `positional-arg-name:"SOURCE-APP" required:"true" description:"The old application name"`
	TargetAppName string `positional-arg-name:"TARGET-NAME" required:"true" description:"The new application name"`
}

type CreateServiceArgs struct {
	ServiceOffering string `positional-arg-name:"SERVICE" required:"true" description:"The service offering"`
	ServicePlan     string `positional-arg-name:"SERVICE_PLAN" required:"true" description:"The service plan that the service instance will use"`
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type RenameServiceArgs struct {
	ServiceInstance        string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance to rename"`
	NewServiceInstanceName string `positional-arg-name:"NEW_SERVICE_INSTANCE" required:"true" description:"The new name of the service instance"`
}

type BindServiceArgs struct {
	AppName             string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	ServiceInstanceName string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type RouteServiceArgs struct {
	Domain          string `positional-arg-name:"DOMAIN" required:"true" description:"The domain of the route"`
	ServiceInstance string `positional-arg-name:"SERVICE_INSTANCE" required:"true" description:"The service instance"`
}

type AppRenameArgs struct {
	OldAppName string `positional-arg-name:"APP_NAME" required:"true" description:"The old application name"`
	NewAppName string `positional-arg-name:"NEW_APP_NAME" required:"true" description:"The new application name"`
}

type RenameOrgArgs struct {
	OldOrgName string `positional-arg-name:"ORG" required:"true" description:"The old organization name"`
	NewOrgName string `positional-arg-name:"NEW_ORG" required:"true" description:"The new organization name"`
}

type RenameSpaceArgs struct {
	OldSpaceName string `positional-arg-name:"SPACE_NAME" required:"true" description:"The old space name"`
	NewSpaceName string `positional-arg-name:"NEW_SPACE_NAME" required:"true" description:"The new space name"`
}

type SetOrgQuotaArgs struct {
	Organization string `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Quota        string `positional-arg-name:"QUOTA" required:"true" description:"The quota"`
}

type SetSpaceQuotaArgs struct {
	Space      string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space"`
	SpaceQuota string `positional-arg-name:"SPACE_QUOTA" required:"true" description:"The space quota"`
}

type SetHealthCheckArgs struct {
	AppName     string          `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	HealthCheck HealthCheckType `positional-arg-name:"HEALTH_CHECK_TYPE" required:"true" description:"Set to 'port' or 'none'"`
}

type CreateBuildpackArgs struct {
	Buildpack string                      `positional-arg-name:"BUILDPACK" required:"true" description:"The buildpack"`
	Path      PathWithExistenceCheckOrURL `positional-arg-name:"PATH" required:"true" description:"The path to the buildpack file"`
	Position  string                      `positional-arg-name:"POSITION" required:"true" description:"The position that sets priority"`
}

type RenameBuildpackArgs struct {
	OldBuildpackName string `positional-arg-name:"BUILDPACK_NAME" required:"true" description:"The old buildpack name"`
	NewBuildpackName string `positional-arg-name:"NEW_BUILDPACK_NAME" required:"true" description:"The new buildpack name"`
}

type SetOrgRoleArgs struct {
	Username     string  `positional-arg-name:"USERNAME" required:"true" description:"The user"`
	Organization string  `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Role         OrgRole `positional-arg-name:"ROLE" required:"true" description:"The organization role"`
}

type SetSpaceRoleArgs struct {
	Username     string    `positional-arg-name:"USERNAME" required:"true" description:"The user"`
	Organization string    `positional-arg-name:"ORG" required:"true" description:"The organization"`
	Space        string    `positional-arg-name:"SPACE" required:"true" description:"The space"`
	Role         SpaceRole `positional-arg-name:"ROLE" required:"true" description:"The space role"`
}

type ServiceAuthTokenArgs struct {
	Label    string `positional-arg-name:"LABEL" required:"true" description:"The token label"`
	Provider string `positional-arg-name:"PROVIDER" required:"true" description:"The token provider"`
	Token    string `positional-arg-name:"TOKEN" required:"true" description:"The token"`
}

type DeleteServiceAuthTokenArgs struct {
	Label    string `positional-arg-name:"LABEL" required:"true" description:"The token label"`
	Provider string `positional-arg-name:"PROVIDER" required:"true" description:"The token provider"`
}

type ServiceBrokerArgs struct {
	ServiceBroker string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The service broker name"`
	Username      string `positional-arg-name:"USERNAME" required:"true" description:"The username"`
	Password      string `positional-arg-name:"PASSWORD" required:"true" description:"The password"`
	URL           string `positional-arg-name:"URL" required:"true" description:"The URL of the service broker"`
}

type RenameServiceBrokerArgs struct {
	OldServiceBrokerName string `positional-arg-name:"SERVICE_BROKER" required:"true" description:"The old service broker name"`
	NewServiceBrokerName string `positional-arg-name:"NEW_SERVICE_BROKER" required:"true" description:"The new service broker name"`
}

type MigrateServiceInstancesArgs struct {
	V1Service  string `positional-arg-name:"v1_SERVICE" required:"true" description:"The old service offering"`
	V1Provider string `positional-arg-name:"v1_PROVIDER" required:"true" description:"The old service provider"`
	V1Plan     string `positional-arg-name:"v1_PLAN" required:"true" description:"The old service plan"`
	V2Service  string `positional-arg-name:"v2_SERVICE" required:"true" description:"The new service offering"`
	V2Plan     string `positional-arg-name:"v2_PLAN" required:"true" description:"The new service plan"`
}

type SecurityGroupArgs struct {
	SecurityGroup   string                 `positional-arg-name:"SECURITY_GROUP" required:"true" description:"The security group"`
	PathToJsonRules PathWithExistenceCheck `positional-arg-name:"PATH_TO_JSON_RULES_FILE" required:"true" description:"Path to file of JSON describing security group rules"`
}

type AddPluginRepoArgs struct {
	PluginRepoName string `positional-arg-name:"REPO_NAME" required:"true" description:"The plugin repo name"`
	PluginRepoURL  string `positional-arg-name:"URL" required:"true" description:"The URL to the plugin repo"`
}

type InstallPluginArgs struct {
	PluginNameOrLocation Path `positional-arg-name:"PLUGIN_NAME_OR_LOCATION" required:"true" description:"The local path to the plugin, if the plugin exists locally; the URL to the plugin, if the plugin exists online; or the plugin name, if a repo is specified"`
}

type RunTaskArgs struct {
	AppName string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	Command string `positional-arg-name:"COMMAND" required:"true" description:"The command to execute"`
}

type TerminateTaskArgs struct {
	AppName    string `positional-arg-name:"APP_NAME" required:"true" description:"The application name"`
	SequenceID string `positional-arg-name:"TASK_ID" required:"true" description:"The task's unique sequence ID"`
}

type IsolationSegmentName struct {
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type OrgIsolationArgs struct {
	OrganizationName     string `positional-arg-name:"ORG_NAME" required:"true" description:"The organization name"`
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type SpaceIsolationArgs struct {
	SpaceName            string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space name"`
	IsolationSegmentName string `positional-arg-name:"SEGMENT_NAME" required:"true" description:"The isolation segment name"`
}

type ResetSpaceIsolationArgs struct {
	SpaceName string `positional-arg-name:"SPACE_NAME" required:"true" description:"The space name"`
}

type ResetOrgDefaultIsolationArgs struct {
	OrgName string `positional-arg-name:"ORG_NAME" required:"true" description:"The organization name"`
}
<|endoftext|>


<commit_before>package trello

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jesselucas/slackcmd/slack"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
)

type Command struct {
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {
	slackAPIKey := os.Getenv("SLACK_KEY_TRELLO")
	trelloKey := os.Getenv("TRELLO_KEY")
	trelloToken := os.Getenv("TRELLO_TOKEN")
	trelloOrg := "forestgiant"
	if slackAPIKey == "" || trelloKey == "" || trelloToken == "" {
		panic("Missing required environment variable")
	}

	// Verify the request is coming from Slack
	if sc.Token != slackAPIKey {
		err := errors.New("Unauthorized Slack")
		return nil, err
	}

	// create payload
	cp := &slack.CommandPayload{
		Channel:       fmt.Sprintf("@%v", sc.UserName),
		Username:      "FG Bot",
		Emoji:         ":fgdot:",
		SlashResponse: true,
		SendPayload:   false,
	}

	fmt.Println("sc.Text?", sc.Text)

	// TODO refact c and commands variable names

	c := strings.Fields(sc.Text)
	var flags []string
	var noFlags []string

	for _, value := range c {
		if strings.HasPrefix(value, "-") {
			flags = append(flags, value)
		} else {
			noFlags = append(noFlags, value)
		}
	}

	if len(flags) > 0 {
		for _, flag := range flags {
			switch flag {
			case "-c":
				fmt.Println("send to payload channel")
				cp.Channel = fmt.Sprintf("#%v", sc.ChannelName)
				cp.SendPayload = true
				cp.SlashResponse = false
			case "-p":
				fmt.Println("send payload to user")
				cp.SendPayload = true
				cp.SlashResponse = false
			}
		}
	}

	c = noFlags

	fmt.Println("c strings?", c)

	// replace all underscores "_" with spaces " " in commands
	commands := make([]string, len(c))
	copy(commands, c)

	for i := 0; i < len(c); i++ {
		c[i] = strings.Replace(c[i], "_", " ", -1)
	}

	// construct url for Trello
	url := fmt.Sprintf(
		"https://api.trello.com/1/organizations/%v/boards/?fields=name&filter=open&key=%v&token=%v",
		trelloOrg,
		trelloKey,
		trelloToken,
	)

	res, err := http.Get(url)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	// found boards return if only sent one command
	var boards []board
	json.Unmarshal(body, &boards)

	var responseString string

	// if the command is blank return possible commands(boards)
	if len(c) == 0 {

		// iterate over boards and create string to send
		for _, board := range boards {
			responseString += fmt.Sprint(board)
		}

		cp.Text = formatForSlack(c, responseString)

		return cp, nil
	}

	// if there is a command to access a board

	// first check if the command is a valid board
	var foundBoard board

	// check to see if the command passed is a list in the board
	for _, board := range boards {
		if strings.EqualFold(board.Name, c[0]) == true {
			foundBoard = board
			break
		}
	}

	// if nothing matches return
	if foundBoard.Name == "" {
		cp.Text = "invalid board name"
		return cp, nil
	}

	// first make sure the command is a valid list in the wiki board
	url = fmt.Sprintf(
		"https://api.trello.com/1/boards/%v/lists/?fields=name,idBoard&key=%v&token=%v",
		foundBoard.Id,
		trelloKey,
		trelloToken,
	)

	// fmt.Println("url: ", url)

	res, err = http.Get(url)
	body, _ = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var lists []list
	json.Unmarshal(body, &lists)

	// if the second command is black return lists
	if len(c) == 1 {

		fmt.Println("check command lenght", c)

		// iterate over boards and create string to send
		for _, list := range lists {
			responseString += fmt.Sprint(list)
		}

		cp.Text = formatForSlack(commands, responseString)

		return cp, nil
	}

	// if there is a command to access a list
	var foundList list

	// check to see if the command passed is a list in the board
	for _, list := range lists {
		if strings.EqualFold(list.Name, c[1]) == true {
			foundList = list
			break
		}
	}

	// if nothing matches return
	if foundList.Name == "" {
		cp.Text = "invalid board name"
		return cp, nil
	}

	// now look up cards in list
	url = fmt.Sprintf(
		"https://api.trello.com/1/lists/%v/?fields=name&cards=open&card_fields=name&key=%v&token=%v",
		foundList.Id,
		trelloKey,
		trelloToken,
	)
	res, err = http.Get(url)
	body, _ = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	json.Unmarshal(body, &foundList)

	// iterate over boards and create string to send
	for _, card := range foundList.Cards {
		// check if they are urls
		card.URL = IsURL(card.Name)
		responseString += fmt.Sprint(card)
	}

	cp.Text = formatForSlack(commands, responseString)

	return cp, nil

}

func formatForSlack(c []string, s string) string {
	path := strings.Join(c, " ")
	return fmt.Sprintf("/fg %v ```%v```", path, s)
}

// IsUrl test if the rxURL regular expression matches a string
func IsURL(s string) bool {
	rxURL := regexp.MustCompile(`\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))`)

	if s == "" || !strings.HasPrefix(s, "http") {
		return false
	}

	if len(s) >= 2083 || len(s) <= 10 {
		return false
	}

	u, err := url.Parse(s)
	if err != nil {
		return false
	}
	if strings.HasPrefix(u.Host, ".") {
		return false
	}
	return rxURL.MatchString(s)
}

type board struct {
	Name string
	Id   string
}

func (b board) String() string {
	return fmt.Sprintf(
		"• <https://trello.com/b/%v|%v> \n",
		b.Id,
		strings.Replace(b.Name, " ", "_", -1),
	)
}

type list struct {
	Name    string
	Id      string
	IdBoard string
	Cards   []card
}

func (l list) String() string {
	return fmt.Sprintf(
		"• <https://trello.com/b/%v|%v> \n",
		l.IdBoard,
		strings.Replace(l.Name, " ", "_", -1),
	)
}

type card struct {
	Name string
	Id   string
	URL  bool
}

func (c card) String() string {
	var url string

	url = fmt.Sprintf("https://trello.com/c/%v", c.Id)

	if c.URL {
		return fmt.Sprintf(
			"• <%v|%v> [<%v|edit>] \n",
			c.Name,
			c.Name,
			url,
		)

	}

	return fmt.Sprintf(
		"• %v [<%v|edit>] \n",
		c.Name,
		url,
	)
}

// func formatStringForSlack(s string) string {
// 	// 	& replaced with &
// 	// < replaced with <
// 	// > replaced with >

// 	strings.Replace(s, "&", "&", -1)
// 	strings.Replace(s, "<", "<", -1)
// 	strings.Replace(s, ">", ">/", -1)

// 	return s
// }
<commit_msg>Updating response output for an empty trello list (no cards to be displayed).<commit_after>package trello

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jesselucas/slackcmd/slack"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
)

type Command struct {
}

func (cmd Command) Request(sc *slack.SlashCommand) (*slack.CommandPayload, error) {
	slackAPIKey := os.Getenv("SLACK_KEY_TRELLO")
	trelloKey := os.Getenv("TRELLO_KEY")
	trelloToken := os.Getenv("TRELLO_TOKEN")
	trelloOrg := "forestgiant"
	if slackAPIKey == "" || trelloKey == "" || trelloToken == "" {
		panic("Missing required environment variable")
	}

	// Verify the request is coming from Slack
	if sc.Token != slackAPIKey {
		err := errors.New("Unauthorized Slack")
		return nil, err
	}

	// create payload
	cp := &slack.CommandPayload{
		Channel:       fmt.Sprintf("@%v", sc.UserName),
		Username:      "FG Bot",
		Emoji:         ":fgdot:",
		SlashResponse: true,
		SendPayload:   false,
	}

	fmt.Println("sc.Text?", sc.Text)

	// TODO refact c and commands variable names

	c := strings.Fields(sc.Text)
	var flags []string
	var noFlags []string

	for _, value := range c {
		if strings.HasPrefix(value, "-") {
			flags = append(flags, value)
		} else {
			noFlags = append(noFlags, value)
		}
	}

	if len(flags) > 0 {
		for _, flag := range flags {
			switch flag {
			case "-c":
				fmt.Println("send to payload channel")
				cp.Channel = fmt.Sprintf("#%v", sc.ChannelName)
				cp.SendPayload = true
				cp.SlashResponse = false
			case "-p":
				fmt.Println("send payload to user")
				cp.SendPayload = true
				cp.SlashResponse = false
			}
		}
	}

	c = noFlags

	fmt.Println("c strings?", c)

	// replace all underscores "_" with spaces " " in commands
	commands := make([]string, len(c))
	copy(commands, c)

	for i := 0; i < len(c); i++ {
		c[i] = strings.Replace(c[i], "_", " ", -1)
	}

	// construct url for Trello
	url := fmt.Sprintf(
		"https://api.trello.com/1/organizations/%v/boards/?fields=name&filter=open&key=%v&token=%v",
		trelloOrg,
		trelloKey,
		trelloToken,
	)

	res, err := http.Get(url)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	// found boards return if only sent one command
	var boards []board
	json.Unmarshal(body, &boards)

	var responseString string

	// if the command is blank return possible commands(boards)
	if len(c) == 0 {

		// iterate over boards and create string to send
		for _, board := range boards {
			responseString += fmt.Sprint(board)
		}

		cp.Text = formatForSlack(c, responseString)

		return cp, nil
	}

	// if there is a command to access a board

	// first check if the command is a valid board
	var foundBoard board

	// check to see if the command passed is a list in the board
	for _, board := range boards {
		if strings.EqualFold(board.Name, c[0]) == true {
			foundBoard = board
			break
		}
	}

	// if nothing matches return
	if foundBoard.Name == "" {
		cp.Text = "invalid board name"
		return cp, nil
	}

	// first make sure the command is a valid list in the wiki board
	url = fmt.Sprintf(
		"https://api.trello.com/1/boards/%v/lists/?fields=name,idBoard&key=%v&token=%v",
		foundBoard.Id,
		trelloKey,
		trelloToken,
	)

	// fmt.Println("url: ", url)

	res, err = http.Get(url)
	body, _ = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var lists []list
	json.Unmarshal(body, &lists)

	// if the second command is black return lists
	if len(c) == 1 {

		fmt.Println("check command lenght", c)

		// iterate over boards and create string to send
		for _, list := range lists {
			responseString += fmt.Sprint(list)
		}

		cp.Text = formatForSlack(commands, responseString)

		return cp, nil
	}

	// if there is a command to access a list
	var foundList list

	// check to see if the command passed is a list in the board
	for _, list := range lists {
		if strings.EqualFold(list.Name, c[1]) == true {
			foundList = list
			break
		}
	}

	// if nothing matches return
	if foundList.Name == "" {
		cp.Text = "invalid board name"
		return cp, nil
	}

	// now look up cards in list
	url = fmt.Sprintf(
		"https://api.trello.com/1/lists/%v/?fields=name&cards=open&card_fields=name&key=%v&token=%v",
		foundList.Id,
		trelloKey,
		trelloToken,
	)
	res, err = http.Get(url)
	body, _ = ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	json.Unmarshal(body, &foundList)

	// iterate over boards and create string to send
	for _, card := range foundList.Cards {
		// check if they are urls
		card.URL = IsURL(card.Name)
		responseString += fmt.Sprint(card)
	}

	if responseString == "" {
		cp.Text = formatForSlack(commands, "This list has no cards to display.")
		return cp, nil
	}

	cp.Text = formatForSlack(commands, responseString)

	return cp, nil

}

func formatForSlack(c []string, s string) string {
	path := strings.Join(c, " ")
	return fmt.Sprintf("/fg %v ```%v```", path, s)
}

// IsUrl test if the rxURL regular expression matches a string
func IsURL(s string) bool {
	rxURL := regexp.MustCompile(`\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))`)

	if s == "" || !strings.HasPrefix(s, "http") {
		return false
	}

	if len(s) >= 2083 || len(s) <= 10 {
		return false
	}

	u, err := url.Parse(s)
	if err != nil {
		return false
	}
	if strings.HasPrefix(u.Host, ".") {
		return false
	}
	return rxURL.MatchString(s)
}

type board struct {
	Name string
	Id   string
}

func (b board) String() string {
	return fmt.Sprintf(
		"• <https://trello.com/b/%v|%v> \n",
		b.Id,
		strings.Replace(b.Name, " ", "_", -1),
	)
}

type list struct {
	Name    string
	Id      string
	IdBoard string
	Cards   []card
}

func (l list) String() string {
	return fmt.Sprintf(
		"• <https://trello.com/b/%v|%v> \n",
		l.IdBoard,
		strings.Replace(l.Name, " ", "_", -1),
	)
}

type card struct {
	Name string
	Id   string
	URL  bool
}

func (c card) String() string {
	var url string

	url = fmt.Sprintf("https://trello.com/c/%v", c.Id)

	if c.URL {
		return fmt.Sprintf(
			"• <%v|%v> [<%v|edit>] \n",
			c.Name,
			c.Name,
			url,
		)

	}

	return fmt.Sprintf(
		"• %v [<%v|edit>] \n",
		c.Name,
		url,
	)
}

// func formatStringForSlack(s string) string {
// 	// 	& replaced with &
// 	// < replaced with <
// 	// > replaced with >

// 	strings.Replace(s, "&", "&", -1)
// 	strings.Replace(s, "<", "<", -1)
// 	strings.Replace(s, ">", ">/", -1)

// 	return s
// }
<|endoftext|>


<commit_before>package io

import (
	"io"
	"sync"

	"v2ray.com/core/common/alloc"
)

type ChainWriter struct {
	sync.Mutex
	writer Writer
}

func NewChainWriter(writer Writer) *ChainWriter {
	return &ChainWriter{
		writer: writer,
	}
}

func (this *ChainWriter) Write(payload []byte) (int, error) {
	this.Lock()
	defer this.Unlock()
	if this.writer == nil {
		return 0, io.ErrClosedPipe
	}

	size := len(payload)
	for size > 0 {
		buffer := alloc.NewBuffer().Clear()
		if size > alloc.BufferSize {
			buffer.Append(payload[:alloc.BufferSize])
			size -= alloc.BufferSize
			payload = payload[alloc.BufferSize:]
		} else {
			buffer.Append(payload)
			size = 0
		}
		err := this.writer.Write(buffer)
		if err != nil {
			return 0, err
		}
	}

	return size, nil
}

func (this *ChainWriter) Release() {
	this.Lock()
	this.writer.Release()
	this.writer = nil
	this.Unlock()
}
<commit_msg>return correct number of bytes written<commit_after>package io

import (
	"io"
	"sync"

	"v2ray.com/core/common/alloc"
)

type ChainWriter struct {
	sync.Mutex
	writer Writer
}

func NewChainWriter(writer Writer) *ChainWriter {
	return &ChainWriter{
		writer: writer,
	}
}

func (this *ChainWriter) Write(payload []byte) (int, error) {
	this.Lock()
	defer this.Unlock()
	if this.writer == nil {
		return 0, io.ErrClosedPipe
	}

	bytesWritten := 0
	size := len(payload)
	for size > 0 {
		buffer := alloc.NewBuffer().Clear()
		if size > alloc.BufferSize {
			buffer.Append(payload[:alloc.BufferSize])
			size -= alloc.BufferSize
			payload = payload[alloc.BufferSize:]
			bytesWritten += alloc.BufferSize
		} else {
			buffer.Append(payload)
			bytesWritten += size
			size = 0
		}
		err := this.writer.Write(buffer)
		if err != nil {
			return bytesWritten, err
		}
	}

	return bytesWritten, nil
}

func (this *ChainWriter) Release() {
	this.Lock()
	this.writer.Release()
	this.writer = nil
	this.Unlock()
}
<|endoftext|>


<commit_before>package proto

import(
	pb "goprotobuf.googlecode.com/hg/proto"
	"io/ioutil"
	"path/filepath"
	"fmt"
	"os"
	"strings"
)

func NewMixer(path string) (*Mixer) {
	version, err := ioutil.ReadFile(path + "/VERSION")

	if err != nil {
		panic("Couldn't find a version for mixer:" + path + ":" + err.String() )
	}

	_, name := filepath.Split(path)

	return &Mixer{
	Name: pb.String(name),
	Version: pb.String( string(version) ),
	Assets: make([]*File,0),
	Features: make([]*Feature,0),
	Recipes: make([]*Recipe,0),
	Rewriters: make([]*File,0),
	Package: nil,
	}

}

func ResolveMixer(name string) (*Mixer) {
	path := os.ShellExpand("$GOHATTAN_DATA/mixers/")

	if !strings.Contains(name, ".mxr") {
		name = name + ".mxr"
	}

	fullPath := filepath.Join(path, name)
	return OpenMixer(fullPath)
}

func (m *Mixer) Write(path string) (outputPath string) {

	name := pb.GetString(m.Name)
	version := pb.GetString(m.Version)
	outputFilename := filepath.Join(path, name + "-" + version + ".mxr")

	bytes, err := pb.Marshal(m)

	if err != nil {
		panic("Could not marshal mixer (" + name + "), " + err.String())
	}

	bytes = Encrypt(bytes)

	ioutil.WriteFile(outputFilename, bytes, uint32(0666) )

	return outputFilename
}

func OpenMixer(location string) (m *Mixer) {

	data, err := ioutil.ReadFile(location)

	if err != nil {
		panic("Could not find mixer file:" + location)
	}

	data = Decrypt(data)

	thisMixer := &Mixer{}
	err = pb.Unmarshal(data, thisMixer)

	if err != nil {
		panic("Error unmarshalling mixer at:" + location)
	}

	return thisMixer
}

func (m *Mixer) Inspect(printFunctions bool) {
	fmt.Printf(" \tFeatures: \n\t\t%v\n", m.Features)

	println("\tAssets:")
	for _, asset := range(m.Assets) {
		fmt.Printf("\t\t -- %v\n", pb.GetString(asset.Path) )
	}

	println("\tRecipes:")
	for _, recipe := range(m.Recipes) {
		fmt.Printf("\t\t -- Recipe (%v) contains (%v) files.\n", pb.GetString(recipe.Name), len(recipe.Files) )
	}

	println("\tRewriters:")
	for _, rewriter := range(m.Rewriters) {
		fmt.Printf("\t\t -- %v\n", pb.GetString(rewriter.Path) )
	}

	println("\tRoot Package:")
	if m.Package != nil {
		fmt.Printf("\t\t -- Name: %v\n", pb.GetString(m.Package.Name) )
		fmt.Printf("\t\t -- Types: %v\n", m.Package.Types )
		fmt.Printf("\t\t -- Dependencies:  %v\n", m.Package.Dependencies )
		if printFunctions {
			fmt.Printf("\t\t -- Functions:  %v\n", m.Package.Dependencies )
			for _, function := range(m.Package.Functions) {
				fmt.Printf("\t\t\t %v\n", function.Stub(m.Package) )
			}
		}
	}

}

/* Dummy encryption for now */

func Encrypt(data []byte) []byte{
	for index, b := range(data) {
		data[index] = rot13(b)
	}
	return data
}

func Decrypt(data []byte) []byte{
	for index, b := range(data) {
		data[index] = rot13(b)
	}
	return data
}

func rot13(b byte) byte {
    	if 'a' <= b && b <= 'z' {
    		b = 'a' + ((b-'a')+13)%26
    	}
    	if 'A' <= b && b <= 'Z' {
    		b = 'A' + ((b-'A')+13)%26
    	}
    	return b
}<commit_msg>Update function output within Inspect()<commit_after>package proto

import(
	pb "goprotobuf.googlecode.com/hg/proto"
	"io/ioutil"
	"path/filepath"
	"fmt"
	"os"
	"strings"
)

func NewMixer(path string) (*Mixer) {
	version, err := ioutil.ReadFile(path + "/VERSION")

	if err != nil {
		panic("Couldn't find a version for mixer:" + path + ":" + err.String() )
	}

	_, name := filepath.Split(path)

	return &Mixer{
	Name: pb.String(name),
	Version: pb.String( string(version) ),
	Assets: make([]*File,0),
	Features: make([]*Feature,0),
	Recipes: make([]*Recipe,0),
	Rewriters: make([]*File,0),
	Package: nil,
	}

}

func ResolveMixer(name string) (*Mixer) {
	path := os.ShellExpand("$GOHATTAN_DATA/mixers/")

	if !strings.Contains(name, ".mxr") {
		name = name + ".mxr"
	}

	fullPath := filepath.Join(path, name)
	return OpenMixer(fullPath)
}

func (m *Mixer) Write(path string) (outputPath string) {

	name := pb.GetString(m.Name)
	version := pb.GetString(m.Version)
	outputFilename := filepath.Join(path, name + "-" + version + ".mxr")

	bytes, err := pb.Marshal(m)

	if err != nil {
		panic("Could not marshal mixer (" + name + "), " + err.String())
	}

	bytes = Encrypt(bytes)

	ioutil.WriteFile(outputFilename, bytes, uint32(0666) )

	return outputFilename
}

func OpenMixer(location string) (m *Mixer) {

	data, err := ioutil.ReadFile(location)

	if err != nil {
		panic("Could not find mixer file:" + location)
	}

	data = Decrypt(data)

	thisMixer := &Mixer{}
	err = pb.Unmarshal(data, thisMixer)

	if err != nil {
		panic("Error unmarshalling mixer at:" + location)
	}

	return thisMixer
}

func (m *Mixer) Inspect(printFunctions bool) {
	fmt.Printf(" \tFeatures: \n\t\t%v\n", m.Features)

	println("\tAssets:")
	for _, asset := range(m.Assets) {
		fmt.Printf("\t\t -- %v\n", pb.GetString(asset.Path) )
	}

	println("\tRecipes:")
	for _, recipe := range(m.Recipes) {
		fmt.Printf("\t\t -- Recipe (%v) contains (%v) files.\n", pb.GetString(recipe.Name), len(recipe.Files) )
	}

	println("\tRewriters:")
	for _, rewriter := range(m.Rewriters) {
		fmt.Printf("\t\t -- %v\n", pb.GetString(rewriter.Path) )
	}

	println("\tRoot Package:")
	if m.Package != nil {
		fmt.Printf("\t\t -- Name: %v\n", pb.GetString(m.Package.Name) )
		fmt.Printf("\t\t -- Types: %v\n", m.Package.Types )
		fmt.Printf("\t\t -- Dependencies:  %v\n", m.Package.Dependencies )
		if printFunctions {
			fmt.Printf("\t\t -- Functions (%v):\n", len(m.Package.Functions))
			for _, function := range(m.Package.Functions) {
				fmt.Printf("\t\t\t %v\n", function.Stub(m.Package) )
			}
		}
	}

}

/* Dummy encryption for now */

func Encrypt(data []byte) []byte{
	for index, b := range(data) {
		data[index] = rot13(b)
	}
	return data
}

func Decrypt(data []byte) []byte{
	for index, b := range(data) {
		data[index] = rot13(b)
	}
	return data
}

func rot13(b byte) byte {
    	if 'a' <= b && b <= 'z' {
    		b = 'a' + ((b-'a')+13)%26
    	}
    	if 'A' <= b && b <= 'Z' {
    		b = 'A' + ((b-'A')+13)%26
    	}
    	return b
}<|endoftext|>


<commit_before>// All of the methods used to communicate with the digital_ocean API
// are here. Their API is on a path to V2, so just plain JSON is used
// in place of a proper client library for now.

package digitalocean

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const DIGITALOCEAN_API_URL = "https://api.digitalocean.com"

type Image struct {
	Id           uint
	Name         string
	Distribution string
}

type ImagesResp struct {
	Images []Image
}

type DigitalOceanClient struct {
	// The http client for communicating
	client *http.Client

	// The base URL of the API
	BaseURL string

	// Credentials
	ClientID string
	APIKey   string
}

// Creates a new client for communicating with DO
func (d DigitalOceanClient) New(client string, key string) *DigitalOceanClient {
	c := &DigitalOceanClient{
		client: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
			},
		},
		BaseURL:  DIGITALOCEAN_API_URL,
		ClientID: client,
		APIKey:   key,
	}
	return c
}

// Creates an SSH Key and returns it's id
func (d DigitalOceanClient) CreateKey(name string, pub string) (uint, error) {
	params := url.Values{}
	params.Set("name", name)
	params.Set("ssh_pub_key", pub)

	body, err := NewRequest(d, "ssh_keys/new", params)
	if err != nil {
		return 0, err
	}

	// Read the SSH key's ID we just created
	key := body["ssh_key"].(map[string]interface{})
	keyId := key["id"].(float64)
	return uint(keyId), nil
}

// Destroys an SSH key
func (d DigitalOceanClient) DestroyKey(id uint) error {
	path := fmt.Sprintf("ssh_keys/%v/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Creates a droplet and returns it's id
func (d DigitalOceanClient) CreateDroplet(name string, size uint, image uint, region uint, keyId uint) (uint, error) {
	params := url.Values{}
	params.Set("name", name)
	params.Set("size_id", fmt.Sprintf("%v", size))
	params.Set("image_id", fmt.Sprintf("%v", image))
	params.Set("region_id", fmt.Sprintf("%v", region))
	params.Set("ssh_key_ids", fmt.Sprintf("%v", keyId))

	body, err := NewRequest(d, "droplets/new", params)
	if err != nil {
		return 0, err
	}

	// Read the Droplets ID
	droplet := body["droplet"].(map[string]interface{})
	dropletId := droplet["id"].(float64)
	return uint(dropletId), err
}

// Destroys a droplet
func (d DigitalOceanClient) DestroyDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Powers off a droplet
func (d DigitalOceanClient) PowerOffDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/power_off", id)

	_, err := NewRequest(d, path, url.Values{})

	return err
}

// Shutsdown a droplet. This is a "soft" shutdown.
func (d DigitalOceanClient) ShutdownDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/shutdown", id)

	_, err := NewRequest(d, path, url.Values{})

	return err
}

// Creates a snaphot of a droplet by it's ID
func (d DigitalOceanClient) CreateSnapshot(id uint, name string) error {
	path := fmt.Sprintf("droplets/%v/snapshot", id)

	params := url.Values{}
	params.Set("name", name)

	_, err := NewRequest(d, path, params)

	return err
}

// Returns all available images.
func (d DigitalOceanClient) Images() ([]Image, error) {
	resp, err := NewRequest(d, "images", url.Values{})
	if err != nil {
		return nil, err
	}

	var result ImagesResp
	if err := mapstructure.Decode(resp, &result); err != nil {
		return nil, err
	}

	return result.Images, nil
}

// Destroys an image by its ID.
func (d DigitalOceanClient) DestroyImage(id uint) error {
	path := fmt.Sprintf("images/%d/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Returns DO's string representation of status "off" "new" "active" etc.
func (d DigitalOceanClient) DropletStatus(id uint) (string, string, error) {
	path := fmt.Sprintf("droplets/%v", id)

	body, err := NewRequest(d, path, url.Values{})
	if err != nil {
		return "", "", err
	}

	var ip string

	// Read the droplet's "status"
	droplet := body["droplet"].(map[string]interface{})
	status := droplet["status"].(string)

	if droplet["ip_address"] != nil {
		ip = droplet["ip_address"].(string)
	}

	return ip, status, err
}

// Sends an api request and returns a generic map[string]interface of
// the response.
func NewRequest(d DigitalOceanClient, path string, params url.Values) (map[string]interface{}, error) {
	client := d.client

	// Add the authentication parameters
	params.Set("client_id", d.ClientID)
	params.Set("api_key", d.APIKey)

	url := fmt.Sprintf("%s/%s?%s", DIGITALOCEAN_API_URL, path, params.Encode())

	// Do some basic scrubbing so sensitive information doesn't appear in logs
	scrubbedUrl := strings.Replace(url, d.ClientID, "CLIENT_ID", -1)
	scrubbedUrl = strings.Replace(scrubbedUrl, d.APIKey, "API_KEY", -1)
	log.Printf("sending new request to digitalocean: %s", scrubbedUrl)

	var lastErr error
	for attempts := 1; attempts < 5; attempts++ {
		resp, err := client.Get(url)
		if err != nil {
			return nil, err
		}

		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return nil, err
		}

		log.Printf("response from digitalocean: %s", body)

		var decodedResponse map[string]interface{}
		err = json.Unmarshal(body, &decodedResponse)
		if err != nil {
			err = errors.New(fmt.Sprintf("Failed to decode JSON response (HTTP %v) from DigitalOcean: %s",
				resp.StatusCode, body))
			return decodedResponse, err
		}

		// Check for errors sent by digitalocean
		status := decodedResponse["status"].(string)
		if status == "OK" {
			return decodedResponse, nil
		}

		if status == "ERROR" {
			status = decodedResponse["error_message"].(string)
		}

		lastErr = errors.New(fmt.Sprintf("Received error from DigitalOcean (%d): %s",
			resp.StatusCode, status))
		log.Println(lastErr)
		if strings.Contains(status, "has a pending event") {
			// Retry, DigitalOcean sends these dumb "pending event"
			// errors all the time.
			time.Sleep(5 * time.Second)
			continue
		}

		// Some other kind of error. Just return.
		return decodedResponse, lastErr
	}

	return nil, lastErr
}
<commit_msg>builder/digitalocean: looser pending event string matching.<commit_after>// All of the methods used to communicate with the digital_ocean API
// are here. Their API is on a path to V2, so just plain JSON is used
// in place of a proper client library for now.

package digitalocean

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const DIGITALOCEAN_API_URL = "https://api.digitalocean.com"

type Image struct {
	Id           uint
	Name         string
	Distribution string
}

type ImagesResp struct {
	Images []Image
}

type DigitalOceanClient struct {
	// The http client for communicating
	client *http.Client

	// The base URL of the API
	BaseURL string

	// Credentials
	ClientID string
	APIKey   string
}

// Creates a new client for communicating with DO
func (d DigitalOceanClient) New(client string, key string) *DigitalOceanClient {
	c := &DigitalOceanClient{
		client: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
			},
		},
		BaseURL:  DIGITALOCEAN_API_URL,
		ClientID: client,
		APIKey:   key,
	}
	return c
}

// Creates an SSH Key and returns it's id
func (d DigitalOceanClient) CreateKey(name string, pub string) (uint, error) {
	params := url.Values{}
	params.Set("name", name)
	params.Set("ssh_pub_key", pub)

	body, err := NewRequest(d, "ssh_keys/new", params)
	if err != nil {
		return 0, err
	}

	// Read the SSH key's ID we just created
	key := body["ssh_key"].(map[string]interface{})
	keyId := key["id"].(float64)
	return uint(keyId), nil
}

// Destroys an SSH key
func (d DigitalOceanClient) DestroyKey(id uint) error {
	path := fmt.Sprintf("ssh_keys/%v/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Creates a droplet and returns it's id
func (d DigitalOceanClient) CreateDroplet(name string, size uint, image uint, region uint, keyId uint) (uint, error) {
	params := url.Values{}
	params.Set("name", name)
	params.Set("size_id", fmt.Sprintf("%v", size))
	params.Set("image_id", fmt.Sprintf("%v", image))
	params.Set("region_id", fmt.Sprintf("%v", region))
	params.Set("ssh_key_ids", fmt.Sprintf("%v", keyId))

	body, err := NewRequest(d, "droplets/new", params)
	if err != nil {
		return 0, err
	}

	// Read the Droplets ID
	droplet := body["droplet"].(map[string]interface{})
	dropletId := droplet["id"].(float64)
	return uint(dropletId), err
}

// Destroys a droplet
func (d DigitalOceanClient) DestroyDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Powers off a droplet
func (d DigitalOceanClient) PowerOffDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/power_off", id)

	_, err := NewRequest(d, path, url.Values{})

	return err
}

// Shutsdown a droplet. This is a "soft" shutdown.
func (d DigitalOceanClient) ShutdownDroplet(id uint) error {
	path := fmt.Sprintf("droplets/%v/shutdown", id)

	_, err := NewRequest(d, path, url.Values{})

	return err
}

// Creates a snaphot of a droplet by it's ID
func (d DigitalOceanClient) CreateSnapshot(id uint, name string) error {
	path := fmt.Sprintf("droplets/%v/snapshot", id)

	params := url.Values{}
	params.Set("name", name)

	_, err := NewRequest(d, path, params)

	return err
}

// Returns all available images.
func (d DigitalOceanClient) Images() ([]Image, error) {
	resp, err := NewRequest(d, "images", url.Values{})
	if err != nil {
		return nil, err
	}

	var result ImagesResp
	if err := mapstructure.Decode(resp, &result); err != nil {
		return nil, err
	}

	return result.Images, nil
}

// Destroys an image by its ID.
func (d DigitalOceanClient) DestroyImage(id uint) error {
	path := fmt.Sprintf("images/%d/destroy", id)
	_, err := NewRequest(d, path, url.Values{})
	return err
}

// Returns DO's string representation of status "off" "new" "active" etc.
func (d DigitalOceanClient) DropletStatus(id uint) (string, string, error) {
	path := fmt.Sprintf("droplets/%v", id)

	body, err := NewRequest(d, path, url.Values{})
	if err != nil {
		return "", "", err
	}

	var ip string

	// Read the droplet's "status"
	droplet := body["droplet"].(map[string]interface{})
	status := droplet["status"].(string)

	if droplet["ip_address"] != nil {
		ip = droplet["ip_address"].(string)
	}

	return ip, status, err
}

// Sends an api request and returns a generic map[string]interface of
// the response.
func NewRequest(d DigitalOceanClient, path string, params url.Values) (map[string]interface{}, error) {
	client := d.client

	// Add the authentication parameters
	params.Set("client_id", d.ClientID)
	params.Set("api_key", d.APIKey)

	url := fmt.Sprintf("%s/%s?%s", DIGITALOCEAN_API_URL, path, params.Encode())

	// Do some basic scrubbing so sensitive information doesn't appear in logs
	scrubbedUrl := strings.Replace(url, d.ClientID, "CLIENT_ID", -1)
	scrubbedUrl = strings.Replace(scrubbedUrl, d.APIKey, "API_KEY", -1)
	log.Printf("sending new request to digitalocean: %s", scrubbedUrl)

	var lastErr error
	for attempts := 1; attempts < 5; attempts++ {
		resp, err := client.Get(url)
		if err != nil {
			return nil, err
		}

		body, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return nil, err
		}

		log.Printf("response from digitalocean: %s", body)

		var decodedResponse map[string]interface{}
		err = json.Unmarshal(body, &decodedResponse)
		if err != nil {
			err = errors.New(fmt.Sprintf("Failed to decode JSON response (HTTP %v) from DigitalOcean: %s",
				resp.StatusCode, body))
			return decodedResponse, err
		}

		// Check for errors sent by digitalocean
		status := decodedResponse["status"].(string)
		if status == "OK" {
			return decodedResponse, nil
		}

		if status == "ERROR" {
			status = decodedResponse["error_message"].(string)
		}

		lastErr = errors.New(fmt.Sprintf("Received error from DigitalOcean (%d): %s",
			resp.StatusCode, status))
		log.Println(lastErr)
		if strings.Contains(status, "a pending event") {
			// Retry, DigitalOcean sends these dumb "pending event"
			// errors all the time.
			time.Sleep(5 * time.Second)
			continue
		}

		// Some other kind of error. Just return.
		return decodedResponse, lastErr
	}

	return nil, lastErr
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"time"

	"github.com/docker/libkv/store"
	"github.com/docker/libkv/store/boltdb"
	"github.com/docker/libkv/store/consul"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/hako/durafmt"
	"github.com/joho/godotenv"
	"github.com/metalmatze/alertmanager-bot/pkg/alertmanager"
	"github.com/metalmatze/alertmanager-bot/pkg/telegram"
	"github.com/oklog/run"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"gopkg.in/alecthomas/kingpin.v2"
)

const (
	storeBolt   = "bolt"
	storeConsul = "consul"

	levelDebug = "debug"
	levelInfo  = "info"
	levelWarn  = "warn"
	levelError = "error"
)

var (
	// Version of alertmanager-bot.
	Version string
	// Revision or Commit this binary was built from.
	Revision string
	// BuildDate this binary was built.
	BuildDate string
	// GoVersion running this binary.
	GoVersion = runtime.Version()
	// StartTime has the time this was started.
	StartTime = time.Now()
)

func main() {
	godotenv.Load()

	config := struct {
		alertmanager   *url.URL
		boltPath       string
		consul         *url.URL
		listenAddr     string
		logLevel       string
		logJSON        bool
		store          string
		telegramAdmins []int
		telegramToken  string
		templatesPaths []string
	}{}

	a := kingpin.New("alertmanager-bot", "Bot for Prometheus' Alertmanager")
	a.HelpFlag.Short('h')

	a.Flag("alertmanager.url", "The URL that's used to connect to the alertmanager").
		Required().
		Envar("ALERTMANAGER_URL").
		URLVar(&config.alertmanager)

	a.Flag("bolt.path", "The path to the file where bolt persists its data").
		Envar("BOLT_PATH").
		StringVar(&config.boltPath)

	a.Flag("consul.url", "The URL that's used to connect to the consul store").
		Envar("CONSUL_URL").
		URLVar(&config.consul)

	a.Flag("listen.addr", "The address the alertmanager-bot listens on for incoming webhooks").
		Required().
		Envar("LISTEN_ADDR").
		StringVar(&config.listenAddr)

	a.Flag("log.json", "Tell the application to log json and not key value pairs").
		Envar("LOG_JSON").
		BoolVar(&config.logJSON)

	a.Flag("log.level", "The log level to use for filtering logs").
		Envar("LOG_LEVEL").
		Default(levelInfo).
		EnumVar(&config.logLevel, levelError, levelWarn, levelInfo, levelDebug)

	a.Flag("store", "The store to use").
		Required().
		Envar("STORE").
		EnumVar(&config.store, storeBolt, storeConsul)

	a.Flag("telegram.admin", "The ID of the initial Telegram Admin").
		Required().
		Envar("TELEGRAM_ADMIN").
		IntsVar(&config.telegramAdmins)

	a.Flag("telegram.token", "The token used to connect with Telegram").
		Required().
		Envar("TELEGRAM_TOKEN").
		StringVar(&config.telegramToken)

	a.Flag("template.paths", "The paths to the template").
		Envar("TEMPLATE_PATHS").
		Default("./default.tmpl").
		ExistingFilesVar(&config.templatesPaths)

	_, err := a.Parse(os.Args[1:])
	if err != nil {
		fmt.Printf("error parsing commandline arguments: %v\n", err)
		a.Usage(os.Args[1:])
		os.Exit(2)
	}

	levelFilter := map[string]level.Option{
		levelError: level.AllowError(),
		levelWarn:  level.AllowWarn(),
		levelInfo:  level.AllowInfo(),
		levelDebug: level.AllowDebug(),
	}

	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	if config.logJSON {
		logger = log.NewJSONLogger(log.NewSyncWriter(os.Stderr))
	}

	logger = level.NewFilter(logger, levelFilter[config.logLevel])
	logger = log.With(logger,
		"ts", log.DefaultTimestampUTC,
		"caller", log.DefaultCaller,
	)

	var tmpl *template.Template
	{
		funcs := template.DefaultFuncs
		funcs["since"] = func(t time.Time) string {
			return durafmt.Parse(time.Since(t)).String()
		}
		funcs["duration"] = func(start time.Time, end time.Time) string {
			return durafmt.Parse(end.Sub(start)).String()
		}

		template.DefaultFuncs = funcs

		tmpl, err = template.FromGlobs(config.templatesPaths...)
		if err != nil {
			level.Error(logger).Log("msg", "failed to parse templates", "err", err)
			os.Exit(1)
		}
		tmpl.ExternalURL = config.alertmanager
	}

	var kvStore store.Store
	{
		switch strings.ToLower(config.store) {
		case storeBolt:
			kvStore, err = boltdb.New([]string{config.boltPath}, &store.Config{Bucket: "alertmanager"})
			if err != nil {
				level.Error(logger).Log("msg", "failed to create bolt store backend", "err", err)
				os.Exit(1)
			}
		case storeConsul:
			kvStore, err = consul.New([]string{config.consul.String()}, nil)
			if err != nil {
				level.Error(logger).Log("msg", "failed to create consul store backend", "err", err)
				os.Exit(1)
			}
		default:
			level.Error(logger).Log("msg", "please provide one of the following supported store backends: bolt, consul")
			os.Exit(1)
		}
	}
	defer kvStore.Close()

	ctx, cancel := context.WithCancel(context.Background())

	// TODO Needs fan out for multiple bots
	webhooks := make(chan notify.WebhookMessage, 32)

	var g run.Group
	{
		tlogger := log.With(logger, "component", "telegram")

		chats, err := telegram.NewChatStore(kvStore)
		if err != nil {
			level.Error(logger).Log("msg", "failed to create chat store", "err", err)
			os.Exit(1)
		}

		bot, err := telegram.NewBot(
			chats, config.telegramToken, config.telegramAdmins[0],
			telegram.WithLogger(tlogger),
			telegram.WithAddr(config.listenAddr),
			telegram.WithAlertmanager(config.alertmanager),
			telegram.WithTemplates(tmpl),
			telegram.WithRevision(Revision),
			telegram.WithStartTime(StartTime),
			telegram.WithExtraAdmins(config.telegramAdmins[1:]...),
		)
		if err != nil {
			level.Error(tlogger).Log("msg", "failed to create bot", "err", err)
			os.Exit(2)
		}

		g.Add(func() error {
			level.Info(tlogger).Log(
				"msg", "starting alertmanager-bot",
				"version", Version,
				"revision", Revision,
				"buildDate", BuildDate,
				"goVersion", GoVersion,
			)

			// Runs the bot itself communicating with Telegram
			return bot.Run(ctx, webhooks)
		}, func(err error) {
			cancel()
		})
	}
	{
		wlogger := log.With(logger, "component", "webserver")

		// TODO: Use Heptio's healthcheck library
		handleHealth := func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
		}

		webhooksCounter := prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "alertmanagerbot",
			Name:      "webhooks_total",
			Help:      "Number of webhooks received by this bot",
		})

		prometheus.MustRegister(webhooksCounter)

		m := http.NewServeMux()
		m.HandleFunc("/", alertmanager.HandleWebhook(wlogger, webhooksCounter, webhooks))
		m.Handle("/metrics", promhttp.Handler())
		m.HandleFunc("/health", handleHealth)
		m.HandleFunc("/healthz", handleHealth)

		s := http.Server{
			Addr:    config.listenAddr,
			Handler: m,
		}

		g.Add(func() error {
			level.Info(wlogger).Log("msg", "starting webserver", "addr", config.listenAddr)
			return s.ListenAndServe()
		}, func(err error) {
			s.Shutdown(context.Background())
		})
	}
	{
		sig := make(chan os.Signal)
		signal.Notify(sig, os.Interrupt, os.Kill)

		g.Add(func() error {
			<-sig
			return nil
		}, func(err error) {
			cancel()
			close(sig)
		})
	}

	if err := g.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Update default.tmpl default file path<commit_after>package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"time"

	"github.com/docker/libkv/store"
	"github.com/docker/libkv/store/boltdb"
	"github.com/docker/libkv/store/consul"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/hako/durafmt"
	"github.com/joho/godotenv"
	"github.com/metalmatze/alertmanager-bot/pkg/alertmanager"
	"github.com/metalmatze/alertmanager-bot/pkg/telegram"
	"github.com/oklog/run"
	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/template"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"gopkg.in/alecthomas/kingpin.v2"
)

const (
	storeBolt   = "bolt"
	storeConsul = "consul"

	levelDebug = "debug"
	levelInfo  = "info"
	levelWarn  = "warn"
	levelError = "error"
)

var (
	// Version of alertmanager-bot.
	Version string
	// Revision or Commit this binary was built from.
	Revision string
	// BuildDate this binary was built.
	BuildDate string
	// GoVersion running this binary.
	GoVersion = runtime.Version()
	// StartTime has the time this was started.
	StartTime = time.Now()
)

func main() {
	godotenv.Load()

	config := struct {
		alertmanager   *url.URL
		boltPath       string
		consul         *url.URL
		listenAddr     string
		logLevel       string
		logJSON        bool
		store          string
		telegramAdmins []int
		telegramToken  string
		templatesPaths []string
	}{}

	a := kingpin.New("alertmanager-bot", "Bot for Prometheus' Alertmanager")
	a.HelpFlag.Short('h')

	a.Flag("alertmanager.url", "The URL that's used to connect to the alertmanager").
		Required().
		Envar("ALERTMANAGER_URL").
		URLVar(&config.alertmanager)

	a.Flag("bolt.path", "The path to the file where bolt persists its data").
		Envar("BOLT_PATH").
		StringVar(&config.boltPath)

	a.Flag("consul.url", "The URL that's used to connect to the consul store").
		Envar("CONSUL_URL").
		URLVar(&config.consul)

	a.Flag("listen.addr", "The address the alertmanager-bot listens on for incoming webhooks").
		Required().
		Envar("LISTEN_ADDR").
		StringVar(&config.listenAddr)

	a.Flag("log.json", "Tell the application to log json and not key value pairs").
		Envar("LOG_JSON").
		BoolVar(&config.logJSON)

	a.Flag("log.level", "The log level to use for filtering logs").
		Envar("LOG_LEVEL").
		Default(levelInfo).
		EnumVar(&config.logLevel, levelError, levelWarn, levelInfo, levelDebug)

	a.Flag("store", "The store to use").
		Required().
		Envar("STORE").
		EnumVar(&config.store, storeBolt, storeConsul)

	a.Flag("telegram.admin", "The ID of the initial Telegram Admin").
		Required().
		Envar("TELEGRAM_ADMIN").
		IntsVar(&config.telegramAdmins)

	a.Flag("telegram.token", "The token used to connect with Telegram").
		Required().
		Envar("TELEGRAM_TOKEN").
		StringVar(&config.telegramToken)

	a.Flag("template.paths", "The paths to the template").
		Envar("TEMPLATE_PATHS").
		Default("/templates/default.tmpl").
		ExistingFilesVar(&config.templatesPaths)

	_, err := a.Parse(os.Args[1:])
	if err != nil {
		fmt.Printf("error parsing commandline arguments: %v\n", err)
		a.Usage(os.Args[1:])
		os.Exit(2)
	}

	levelFilter := map[string]level.Option{
		levelError: level.AllowError(),
		levelWarn:  level.AllowWarn(),
		levelInfo:  level.AllowInfo(),
		levelDebug: level.AllowDebug(),
	}

	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
	if config.logJSON {
		logger = log.NewJSONLogger(log.NewSyncWriter(os.Stderr))
	}

	logger = level.NewFilter(logger, levelFilter[config.logLevel])
	logger = log.With(logger,
		"ts", log.DefaultTimestampUTC,
		"caller", log.DefaultCaller,
	)

	var tmpl *template.Template
	{
		funcs := template.DefaultFuncs
		funcs["since"] = func(t time.Time) string {
			return durafmt.Parse(time.Since(t)).String()
		}
		funcs["duration"] = func(start time.Time, end time.Time) string {
			return durafmt.Parse(end.Sub(start)).String()
		}

		template.DefaultFuncs = funcs

		tmpl, err = template.FromGlobs(config.templatesPaths...)
		if err != nil {
			level.Error(logger).Log("msg", "failed to parse templates", "err", err)
			os.Exit(1)
		}
		tmpl.ExternalURL = config.alertmanager
	}

	var kvStore store.Store
	{
		switch strings.ToLower(config.store) {
		case storeBolt:
			kvStore, err = boltdb.New([]string{config.boltPath}, &store.Config{Bucket: "alertmanager"})
			if err != nil {
				level.Error(logger).Log("msg", "failed to create bolt store backend", "err", err)
				os.Exit(1)
			}
		case storeConsul:
			kvStore, err = consul.New([]string{config.consul.String()}, nil)
			if err != nil {
				level.Error(logger).Log("msg", "failed to create consul store backend", "err", err)
				os.Exit(1)
			}
		default:
			level.Error(logger).Log("msg", "please provide one of the following supported store backends: bolt, consul")
			os.Exit(1)
		}
	}
	defer kvStore.Close()

	ctx, cancel := context.WithCancel(context.Background())

	// TODO Needs fan out for multiple bots
	webhooks := make(chan notify.WebhookMessage, 32)

	var g run.Group
	{
		tlogger := log.With(logger, "component", "telegram")

		chats, err := telegram.NewChatStore(kvStore)
		if err != nil {
			level.Error(logger).Log("msg", "failed to create chat store", "err", err)
			os.Exit(1)
		}

		bot, err := telegram.NewBot(
			chats, config.telegramToken, config.telegramAdmins[0],
			telegram.WithLogger(tlogger),
			telegram.WithAddr(config.listenAddr),
			telegram.WithAlertmanager(config.alertmanager),
			telegram.WithTemplates(tmpl),
			telegram.WithRevision(Revision),
			telegram.WithStartTime(StartTime),
			telegram.WithExtraAdmins(config.telegramAdmins[1:]...),
		)
		if err != nil {
			level.Error(tlogger).Log("msg", "failed to create bot", "err", err)
			os.Exit(2)
		}

		g.Add(func() error {
			level.Info(tlogger).Log(
				"msg", "starting alertmanager-bot",
				"version", Version,
				"revision", Revision,
				"buildDate", BuildDate,
				"goVersion", GoVersion,
			)

			// Runs the bot itself communicating with Telegram
			return bot.Run(ctx, webhooks)
		}, func(err error) {
			cancel()
		})
	}
	{
		wlogger := log.With(logger, "component", "webserver")

		// TODO: Use Heptio's healthcheck library
		handleHealth := func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
		}

		webhooksCounter := prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "alertmanagerbot",
			Name:      "webhooks_total",
			Help:      "Number of webhooks received by this bot",
		})

		prometheus.MustRegister(webhooksCounter)

		m := http.NewServeMux()
		m.HandleFunc("/", alertmanager.HandleWebhook(wlogger, webhooksCounter, webhooks))
		m.Handle("/metrics", promhttp.Handler())
		m.HandleFunc("/health", handleHealth)
		m.HandleFunc("/healthz", handleHealth)

		s := http.Server{
			Addr:    config.listenAddr,
			Handler: m,
		}

		g.Add(func() error {
			level.Info(wlogger).Log("msg", "starting webserver", "addr", config.listenAddr)
			return s.ListenAndServe()
		}, func(err error) {
			s.Shutdown(context.Background())
		})
	}
	{
		sig := make(chan os.Signal)
		signal.Notify(sig, os.Interrupt, os.Kill)

		g.Add(func() error {
			<-sig
			return nil
		}, func(err error) {
			cancel()
			close(sig)
		})
	}

	if err := g.Run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The debugnewvm command creates and destroys a VM-based GCE buildlet
// with lots of logging for debugging. Nothing depends on this.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
	"golang.org/x/build/dashboard"
	"golang.org/x/build/internal/buildgo"
	"golang.org/x/oauth2"
	compute "google.golang.org/api/compute/v1"
)

var (
	hostType      = flag.String("host", "", "host type to create")
	overrideImage = flag.String("override-image", "", "if non-empty, an alternate GCE VM image or container image to use, depending on the host type")
	serial        = flag.Bool("serial", true, "watch serial")
	pauseAfterUp  = flag.Duration("pause-after-up", 0, "pause for this duration before buildlet is destroyed")

	runBuild = flag.String("run-build", "", "optional builder name to run all.bash for")
	buildRev = flag.String("rev", "master", "if --run-build is specified, the git hash or branch name to build")
)

var (
	computeSvc *compute.Service
	env        *buildenv.Environment
)

func main() {
	buildenv.RegisterFlags()
	flag.Parse()

	var bconf *dashboard.BuildConfig
	if *runBuild != "" {
		var ok bool
		bconf, ok = dashboard.Builders[*runBuild]
		if !ok {
			log.Fatalf("unknown builder %q", *runBuild)
		}
		if *hostType == "" {
			*hostType = bconf.HostType
		}
	}

	if *hostType == "" {
		log.Fatalf("missing --host (or --run-build)")
	}

	hconf, ok := dashboard.Hosts[*hostType]
	if !ok {
		log.Fatalf("unknown host type %q", *hostType)
	}
	if !hconf.IsVM() && !hconf.IsContainer() {
		log.Fatalf("host type %q is type %q; want a VM or container host type", *hostType, hconf.PoolName())
	}
	if img := *overrideImage; img != "" {
		if hconf.IsContainer() {
			hconf.ContainerImage = img
		} else {
			hconf.VMImage = img
		}
	}
	vmImageSummary := hconf.VMImage
	if hconf.IsContainer() {
		vmImageSummary = fmt.Sprintf("cos-stable, running %v", hconf.ContainerImage)
	}

	env = buildenv.FromFlags()
	ctx := context.Background()

	buildenv.CheckUserCredentials()
	creds, err := env.Credentials(ctx)
	if err != nil {
		log.Fatal(err)
	}
	computeSvc, _ = compute.New(oauth2.NewClient(ctx, creds.TokenSource))

	name := fmt.Sprintf("debug-temp-%d", time.Now().Unix())

	log.Printf("Creating %s (with VM image %q)", name, vmImageSummary)
	bc, err := buildlet.StartNewVM(creds, env, name, *hostType, buildlet.VMOpts{
		OnInstanceRequested: func() { log.Printf("instance requested") },
		OnInstanceCreated: func() {
			log.Printf("instance created")
			if *serial {
				go watchSerial(name)
			}
		},
		OnGotInstanceInfo: func() { log.Printf("got instance info") },
		OnBeginBuildletProbe: func(buildletURL string) {
			log.Printf("About to hit %s to see if buildlet is up yet...", buildletURL)
		},
		OnEndBuildletProbe: func(res *http.Response, err error) {
			if err != nil {
				log.Printf("client buildlet probe error: %v", err)
				return
			}
			log.Printf("buildlet probe: %s", res.Status)
		},
	})
	if err != nil {
		log.Fatalf("StartNewVM: %v", err)
	}
	dir, err := bc.WorkDir()
	log.Printf("WorkDir: %v, %v", dir, err)

	var buildFailed bool
	if *runBuild != "" {
		// Push GOROOT_BOOTSTRAP, if needed.
		if u := bconf.GoBootstrapURL(env); u != "" {
			log.Printf("Pushing 'go1.4' Go bootstrap dir ...")
			const bootstrapDir = "go1.4" // might be newer; name is the default
			if err := bc.PutTarFromURL(u, bootstrapDir); err != nil {
				bc.Close()
				log.Fatalf("Putting Go bootstrap: %v", err)
			}
		}

		// Push Go code
		log.Printf("Pushing 'go' dir...")
		goTarGz := "https://go.googlesource.com/go/+archive/" + *buildRev + ".tar.gz"
		if err := bc.PutTarFromURL(goTarGz, "go"); err != nil {
			bc.Close()
			log.Fatalf("Putting go code: %v", err)
		}

		// Push a synthetic VERSION file to prevent git usage:
		if err := bc.PutTar(buildgo.VersionTgz(*buildRev), "go"); err != nil {
			bc.Close()
			log.Fatalf("Putting VERSION file: %v", err)
		}

		allScript := bconf.AllScript()
		log.Printf("Running %s ...", allScript)
		remoteErr, err := bc.Exec(path.Join("go", allScript), buildlet.ExecOpts{
			Output:   os.Stdout,
			ExtraEnv: bconf.Env(),
			Debug:    true,
			Args:     bconf.AllScriptArgs(),
		})
		if err != nil {
			log.Fatalf("error trying to run %s: %v", allScript, err)
		}
		if remoteErr != nil {
			log.Printf("remote failure running %s: %v", allScript, remoteErr)
			buildFailed = true
		}
	}

	if *pauseAfterUp != 0 {
		log.Printf("Sleeping for %v before shutting down...", *pauseAfterUp)
		time.Sleep(*pauseAfterUp)
	}
	if err := bc.Close(); err != nil {
		log.Fatalf("Close: %v", err)
	}
	log.Printf("done.")
	time.Sleep(2 * time.Second) // wait for serial logging to catch up

	if buildFailed {
		os.Exit(1)
	}
}

// watchSerial streams the named VM's serial port to log.Printf. It's roughly:
//   gcloud compute connect-to-serial-port --zone=xxx $NAME
// but in Go and works. For some reason, gcloud doesn't work as a
// child process and has weird errors.
func watchSerial(name string) {
	start := int64(0)
	indent := strings.Repeat(" ", len("2017/07/25 06:37:14 SERIAL: "))
	for {
		sout, err := computeSvc.Instances.GetSerialPortOutput(env.ProjectName, env.Zone, name).Start(start).Do()
		if err != nil {
			log.Printf("serial output error: %v", err)
			return
		}
		moved := sout.Next != start
		start = sout.Next
		contents := strings.Replace(strings.TrimSpace(sout.Contents), "\r\n", "\r\n"+indent, -1)
		if contents != "" {
			log.Printf("SERIAL: %s", contents)
		}
		if !moved {
			time.Sleep(1 * time.Second)
		}
	}
}
<commit_msg>cmd/debugnewvm: add --just-make flag, more timing info in log<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The debugnewvm command creates and destroys a VM-based GCE buildlet
// with lots of logging for debugging. Nothing depends on this.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
	"golang.org/x/build/dashboard"
	"golang.org/x/build/internal/buildgo"
	"golang.org/x/oauth2"
	compute "google.golang.org/api/compute/v1"
)

var (
	hostType      = flag.String("host", "", "host type to create")
	overrideImage = flag.String("override-image", "", "if non-empty, an alternate GCE VM image or container image to use, depending on the host type")
	serial        = flag.Bool("serial", true, "watch serial")
	pauseAfterUp  = flag.Duration("pause-after-up", 0, "pause for this duration before buildlet is destroyed")

	runBuild = flag.String("run-build", "", "optional builder name to run all.bash or make.bash for")
	makeOnly = flag.Bool("make-only", false, "if a --run-build builder name is given, this controls whether make.bash or all.bash is run")
	buildRev = flag.String("rev", "master", "if --run-build is specified, the git hash or branch name to build")
)

var (
	computeSvc *compute.Service
	env        *buildenv.Environment
)

func main() {
	buildenv.RegisterFlags()
	flag.Parse()

	var bconf *dashboard.BuildConfig
	if *runBuild != "" {
		var ok bool
		bconf, ok = dashboard.Builders[*runBuild]
		if !ok {
			log.Fatalf("unknown builder %q", *runBuild)
		}
		if *hostType == "" {
			*hostType = bconf.HostType
		}
	}

	if *hostType == "" {
		log.Fatalf("missing --host (or --run-build)")
	}

	hconf, ok := dashboard.Hosts[*hostType]
	if !ok {
		log.Fatalf("unknown host type %q", *hostType)
	}
	if !hconf.IsVM() && !hconf.IsContainer() {
		log.Fatalf("host type %q is type %q; want a VM or container host type", *hostType, hconf.PoolName())
	}
	if img := *overrideImage; img != "" {
		if hconf.IsContainer() {
			hconf.ContainerImage = img
		} else {
			hconf.VMImage = img
		}
	}
	vmImageSummary := hconf.VMImage
	if hconf.IsContainer() {
		vmImageSummary = fmt.Sprintf("cos-stable, running %v", hconf.ContainerImage)
	}

	env = buildenv.FromFlags()
	ctx := context.Background()

	buildenv.CheckUserCredentials()
	creds, err := env.Credentials(ctx)
	if err != nil {
		log.Fatal(err)
	}
	computeSvc, _ = compute.New(oauth2.NewClient(ctx, creds.TokenSource))

	name := fmt.Sprintf("debug-temp-%d", time.Now().Unix())

	log.Printf("Creating %s (with VM image %q)", name, vmImageSummary)
	bc, err := buildlet.StartNewVM(creds, env, name, *hostType, buildlet.VMOpts{
		OnInstanceRequested: func() { log.Printf("instance requested") },
		OnInstanceCreated: func() {
			log.Printf("instance created")
			if *serial {
				go watchSerial(name)
			}
		},
		OnGotInstanceInfo: func() { log.Printf("got instance info") },
		OnBeginBuildletProbe: func(buildletURL string) {
			log.Printf("About to hit %s to see if buildlet is up yet...", buildletURL)
		},
		OnEndBuildletProbe: func(res *http.Response, err error) {
			if err != nil {
				log.Printf("client buildlet probe error: %v", err)
				return
			}
			log.Printf("buildlet probe: %s", res.Status)
		},
	})
	if err != nil {
		log.Fatalf("StartNewVM: %v", err)
	}
	dir, err := bc.WorkDir()
	log.Printf("WorkDir: %v, %v", dir, err)

	var buildFailed bool
	if *runBuild != "" {
		// Push GOROOT_BOOTSTRAP, if needed.
		if u := bconf.GoBootstrapURL(env); u != "" {
			log.Printf("Pushing 'go1.4' Go bootstrap dir ...")
			const bootstrapDir = "go1.4" // might be newer; name is the default
			if err := bc.PutTarFromURL(u, bootstrapDir); err != nil {
				bc.Close()
				log.Fatalf("Putting Go bootstrap: %v", err)
			}
		}

		// Push Go code
		log.Printf("Pushing 'go' dir...")
		goTarGz := "https://go.googlesource.com/go/+archive/" + *buildRev + ".tar.gz"
		if err := bc.PutTarFromURL(goTarGz, "go"); err != nil {
			bc.Close()
			log.Fatalf("Putting go code: %v", err)
		}

		// Push a synthetic VERSION file to prevent git usage:
		if err := bc.PutTar(buildgo.VersionTgz(*buildRev), "go"); err != nil {
			bc.Close()
			log.Fatalf("Putting VERSION file: %v", err)
		}

		script := bconf.AllScript()
		if *makeOnly {
			script = bconf.MakeScript()
		}
		t0 := time.Now()
		log.Printf("Running %s ...", script)
		remoteErr, err := bc.Exec(path.Join("go", script), buildlet.ExecOpts{
			Output:   os.Stdout,
			ExtraEnv: bconf.Env(),
			Debug:    true,
			Args:     bconf.AllScriptArgs(),
		})
		if err != nil {
			log.Fatalf("error trying to run %s: %v", script, err)
		}
		if remoteErr != nil {
			log.Printf("remote failure running %s: %v", script, remoteErr)
			buildFailed = true
		} else {
			log.Printf("ran %s in %v", script, time.Since(t0).Round(time.Second))
		}
	}

	if *pauseAfterUp != 0 {
		log.Printf("Sleeping for %v before shutting down...", *pauseAfterUp)
		time.Sleep(*pauseAfterUp)
	}
	if err := bc.Close(); err != nil {
		log.Fatalf("Close: %v", err)
	}
	log.Printf("done.")
	time.Sleep(2 * time.Second) // wait for serial logging to catch up

	if buildFailed {
		os.Exit(1)
	}
}

// watchSerial streams the named VM's serial port to log.Printf. It's roughly:
//   gcloud compute connect-to-serial-port --zone=xxx $NAME
// but in Go and works. For some reason, gcloud doesn't work as a
// child process and has weird errors.
func watchSerial(name string) {
	start := int64(0)
	indent := strings.Repeat(" ", len("2017/07/25 06:37:14 SERIAL: "))
	for {
		sout, err := computeSvc.Instances.GetSerialPortOutput(env.ProjectName, env.Zone, name).Start(start).Do()
		if err != nil {
			log.Printf("serial output error: %v", err)
			return
		}
		moved := sout.Next != start
		start = sout.Next
		contents := strings.Replace(strings.TrimSpace(sout.Contents), "\r\n", "\r\n"+indent, -1)
		if contents != "" {
			log.Printf("SERIAL: %s", contents)
		}
		if !moved {
			time.Sleep(1 * time.Second)
		}
	}
}
<|endoftext|>