<commit_before>package testdata

const Signup = `{
  "components": {
    "requestBodies": {
      "SignupCompleteSignupRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CompleteSignupRequest"
            }
          }
        },
        "description": "SignupCompleteSignupRequest"
      },
      "SignupHasPaymentMethodRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/HasPaymentMethodRequest"
            }
          }
        },
        "description": "SignupHasPaymentMethodRequest"
      },
      "SignupRecoverRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RecoverRequest"
            }
          }
        },
        "description": "SignupRecoverRequest"
      },
      "SignupSendVerificationEmailRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SendVerificationEmailRequest"
            }
          }
        },
        "description": "SignupSendVerificationEmailRequest"
      },
      "SignupSetPaymentMethodRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SetPaymentMethodRequest"
            }
          }
        },
        "description": "SignupSetPaymentMethodRequest"
      },
      "SignupVerifyRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VerifyRequest"
            }
          }
        },
        "description": "SignupVerifyRequest"
      }
    },
    "responses": {
      "MicroAPIError": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "Code": {
                  "description": "Error code",
                  "example": 500,
                  "type": "number"
                },
                "Detail": {
                  "description": "Error detail",
                  "example": "service not found",
                  "type": "string"
                },
                "Id": {
                  "description": "Error ID",
                  "type": "string"
                },
                "Status": {
                  "description": "Error status message",
                  "example": "Internal Server Error",
                  "type": "string"
                }
              },
              "title": "MicroAPIError",
              "type": "object"
            }
          }
        },
        "description": "Error from the Micro API"
      },
      "SignupCompleteSignupResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CompleteSignupResponse"
            }
          }
        },
        "description": "SignupCompleteSignupResponse"
      },
      "SignupHasPaymentMethodResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/HasPaymentMethodResponse"
            }
          }
        },
        "description": "SignupHasPaymentMethodResponse"
      },
      "SignupRecoverResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RecoverResponse"
            }
          }
        },
        "description": "SignupRecoverResponse"
      },
      "SignupSendVerificationEmailResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SendVerificationEmailResponse"
            }
          }
        },
        "description": "SignupSendVerificationEmailResponse"
      },
      "SignupSetPaymentMethodResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SetPaymentMethodResponse"
            }
          }
        },
        "description": "SignupSetPaymentMethodResponse"
      },
      "SignupVerifyResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VerifyResponse"
            }
          }
        },
        "description": "SignupVerifyResponse"
      }
    },
    "schemas": {
      "AuthToken": {
        "description": "lifted from https://github.com/micro/go-micro/blob/master/auth/service/proto/auth.proto",
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "created": {
            "format": "int64",
            "type": "number"
          },
          "expiry": {
            "format": "int64",
            "type": "number"
          },
          "refreshToken": {
            "type": "string"
          }
        },
        "title": "AuthToken",
        "type": "object"
      },
      "CompleteSignupRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "namespace": {
            "description": "Which namespace to sign up to based on previous invite",
            "type": "string"
          },
          "paymentMethodID": {
            "description": "This payment method ID is the one we got back from Stripe on the frontend (ie. 'm3o.com/subscribe.html')\n deprecated: signup service now knows the payment method due to the\n SetPaymentMethod call issued by the frontend.",
            "type": "string"
          },
          "secret": {
            "description": "The secret/password to use for the account",
            "type": "string"
          },
          "token": {
            "description": "The token has to be passed here too for identification purposes.",
            "type": "string"
          }
        },
        "title": "CompleteSignupRequest",
        "type": "object"
      },
      "CompleteSignupResponse": {
        "properties": {
          "authToken": {
            "properties": {
              "accessToken": {
                "type": "string"
              },
              "created": {
                "format": "int64",
                "type": "number"
              },
              "expiry": {
                "format": "int64",
                "type": "number"
              },
              "refreshToken": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "namespace": {
            "type": "string"
          }
        },
        "title": "CompleteSignupResponse",
        "type": "object"
      },
      "HasPaymentMethodRequest": {
        "properties": {
          "token": {
            "description": "We can't read by email because that would be too easy to guess.\n The token is already used for identification purposes during the signup\n so we will use that too to pull for the payment method.",
            "type": "string"
          }
        },
        "title": "HasPaymentMethodRequest",
        "type": "object"
      },
      "HasPaymentMethodResponse": {
        "properties": {
          "has": {
            "type": "boolean"
          }
        },
        "title": "HasPaymentMethodResponse",
        "type": "object"
      },
      "RecoverRequest": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "title": "RecoverRequest",
        "type": "object"
      },
      "RecoverResponse": {
        "title": "RecoverResponse",
        "type": "object"
      },
      "SendVerificationEmailRequest": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "title": "SendVerificationEmailRequest",
        "type": "object"
      },
      "SendVerificationEmailResponse": {
        "title": "SendVerificationEmailResponse",
        "type": "object"
      },
      "SetPaymentMethodRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "paymentMethod": {
            "type": "string"
          }
        },
        "title": "SetPaymentMethodRequest",
        "type": "object"
      },
      "SetPaymentMethodResponse": {
        "title": "SetPaymentMethodResponse",
        "type": "object"
      },
      "VerifyRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "token": {
            "description": "Email token that was received in an email.",
            "type": "string"
          }
        },
        "title": "VerifyRequest",
        "type": "object"
      },
      "VerifyResponse": {
        "properties": {
          "authToken": {
            "description": "Auth token to be saved into '~/.micro'\n For users who are already registered and paid,\n the flow stops here.\n For users who are yet to be registered\n the token will be acquired in the 'FinishSignup' step.",
            "properties": {
              "accessToken": {
                "type": "string"
              },
              "created": {
                "format": "int64",
                "type": "number"
              },
              "expiry": {
                "format": "int64",
                "type": "number"
              },
              "refreshToken": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "customerID": {
            "description": "Payment provider custommer id that can be used to\n acquire a payment method, see 'micro login' flow for more.\n @todo this is likely not needed",
            "type": "string"
          },
          "message": {
            "description": "Message to display to the user",
            "type": "string"
          },
          "namespace": {
            "description": "Namespace to use\n @todod deprecated since we no longer support OTP logins",
            "type": "string"
          },
          "namespaces": {
            "description": "Namespaces one has access to based on previous invites\n Currently only 1 is supported",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "paymentRequired": {
            "description": "Whether payment is required or not",
            "type": "boolean"
          }
        },
        "title": "VerifyResponse",
        "type": "object"
      }
    },
    "securitySchemes": {
      "MicroAPIToken": {
        "bearerFormat": "JWT",
        "description": "Micro API token",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Generated by protoc-gen-openapi",
    "title": "Go.Micro.Generator.Test",
    "version": "1",
    "x-logo": {
      "altText": "Micro logo",
      "backgroundColor": "#FFFFFF",
      "url": "https://micro.mu/images/logo.png"
    }
  },
  "openapi": "3.0.0",
  "paths": {
    "/test/Signup/CompleteSignup": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupCompleteSignupRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupCompleteSignupResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.CompleteSignup(CompleteSignupRequest)"
      }
    },
    "/test/Signup/HasPaymentMethod": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupHasPaymentMethodRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupHasPaymentMethodResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.HasPaymentMethod(HasPaymentMethodRequest)"
      }
    },
    "/test/Signup/Recover": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupRecoverRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupRecoverResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.Recover(RecoverRequest)"
      }
    },
    "/test/Signup/SendVerificationEmail": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupSendVerificationEmailRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupSendVerificationEmailResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.SendVerificationEmail(SendVerificationEmailRequest)"
      }
    },
    "/test/Signup/SetPaymentMethod": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupSetPaymentMethodRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupSetPaymentMethodResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.SetPaymentMethod(SetPaymentMethodRequest)"
      }
    },
    "/test/Signup/Verify": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupVerifyRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupVerifyResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.Verify(VerifyRequest)"
      }
    }
  },
  "servers": [
    {
      "url": "https://api.m3o.dev",
      "description": "Micro DEV environment"
    },
    {
      "url": "https://api.m3o.com",
      "description": "Micro LIVE environment"
    }
  ]
}`
<commit_msg>update testdata for openapi<commit_after>package testdata

const Signup = `{
  "components": {
    "requestBodies": {
      "SignupCompleteSignupRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CompleteSignupRequest"
            }
          }
        },
        "description": "SignupCompleteSignupRequest"
      },
      "SignupHasPaymentMethodRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/HasPaymentMethodRequest"
            }
          }
        },
        "description": "SignupHasPaymentMethodRequest"
      },
      "SignupRecoverRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RecoverRequest"
            }
          }
        },
        "description": "SignupRecoverRequest"
      },
      "SignupSendVerificationEmailRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SendVerificationEmailRequest"
            }
          }
        },
        "description": "SignupSendVerificationEmailRequest"
      },
      "SignupSetPaymentMethodRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SetPaymentMethodRequest"
            }
          }
        },
        "description": "SignupSetPaymentMethodRequest"
      },
      "SignupVerifyRequest": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VerifyRequest"
            }
          }
        },
        "description": "SignupVerifyRequest"
      }
    },
    "responses": {
      "MicroAPIError": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "Code": {
                  "description": "Error code",
                  "example": 500,
                  "type": "number"
                },
                "Detail": {
                  "description": "Error detail",
                  "example": "service not found",
                  "type": "string"
                },
                "Id": {
                  "description": "Error ID",
                  "type": "string"
                },
                "Status": {
                  "description": "Error status message",
                  "example": "Internal Server Error",
                  "type": "string"
                }
              },
              "title": "MicroAPIError",
              "type": "object"
            }
          }
        },
        "description": "Error from the Micro API"
      },
      "SignupCompleteSignupResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CompleteSignupResponse"
            }
          }
        },
        "description": "SignupCompleteSignupResponse"
      },
      "SignupHasPaymentMethodResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/HasPaymentMethodResponse"
            }
          }
        },
        "description": "SignupHasPaymentMethodResponse"
      },
      "SignupRecoverResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RecoverResponse"
            }
          }
        },
        "description": "SignupRecoverResponse"
      },
      "SignupSendVerificationEmailResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SendVerificationEmailResponse"
            }
          }
        },
        "description": "SignupSendVerificationEmailResponse"
      },
      "SignupSetPaymentMethodResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SetPaymentMethodResponse"
            }
          }
        },
        "description": "SignupSetPaymentMethodResponse"
      },
      "SignupVerifyResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/VerifyResponse"
            }
          }
        },
        "description": "SignupVerifyResponse"
      }
    },
    "schemas": {
      "AuthToken": {
        "description": "lifted from https://github.com/micro/go-micro/blob/master/auth/service/proto/auth.proto",
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "created": {
            "format": "int64",
            "type": "number"
          },
          "expiry": {
            "format": "int64",
            "type": "number"
          },
          "refreshToken": {
            "type": "string"
          }
        },
        "title": "AuthToken",
        "type": "object"
      },
      "CompleteSignupRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "namespace": {
            "description": "Which namespace to sign up to based on previous invite",
            "type": "string"
          },
          "paymentMethodID": {
            "description": "This payment method ID is the one we got back from Stripe on the frontend (ie. 'm3o.com/subscribe.html')\n deprecated: signup service now knows the payment method due to the\n SetPaymentMethod call issued by the frontend.",
            "type": "string"
          },
          "secret": {
            "description": "The secret/password to use for the account",
            "type": "string"
          },
          "token": {
            "description": "The token has to be passed here too for identification purposes.",
            "type": "string"
          }
        },
        "title": "CompleteSignupRequest",
        "type": "object"
      },
      "CompleteSignupResponse": {
        "properties": {
          "authToken": {
            "properties": {
              "accessToken": {
                "type": "string"
              },
              "created": {
                "format": "int64",
                "type": "number"
              },
              "expiry": {
                "format": "int64",
                "type": "number"
              },
              "refreshToken": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "namespace": {
            "type": "string"
          }
        },
        "title": "CompleteSignupResponse",
        "type": "object"
      },
      "HasPaymentMethodRequest": {
        "properties": {
          "token": {
            "description": "We can't read by email because that would be too easy to guess.\n The token is already used for identification purposes during the signup\n so we will use that too to pull for the payment method.",
            "type": "string"
          }
        },
        "title": "HasPaymentMethodRequest",
        "type": "object"
      },
      "HasPaymentMethodResponse": {
        "properties": {
          "has": {
            "type": "boolean"
          }
        },
        "title": "HasPaymentMethodResponse",
        "type": "object"
      },
      "RecoverRequest": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "title": "RecoverRequest",
        "type": "object"
      },
      "RecoverResponse": {
        "title": "RecoverResponse",
        "type": "object"
      },
      "SendVerificationEmailRequest": {
        "properties": {
          "email": {
            "type": "string"
          }
        },
        "title": "SendVerificationEmailRequest",
        "type": "object"
      },
      "SendVerificationEmailResponse": {
        "title": "SendVerificationEmailResponse",
        "type": "object"
      },
      "SetPaymentMethodRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "paymentMethod": {
            "type": "string"
          }
        },
        "title": "SetPaymentMethodRequest",
        "type": "object"
      },
      "SetPaymentMethodResponse": {
        "title": "SetPaymentMethodResponse",
        "type": "object"
      },
      "VerifyRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "token": {
            "description": "Email token that was received in an email.",
            "type": "string"
          }
        },
        "title": "VerifyRequest",
        "type": "object"
      },
      "VerifyResponse": {
        "properties": {
          "authToken": {
            "description": "Auth token to be saved into '~/.micro'\n For users who are already registered and paid,\n the flow stops here.\n For users who are yet to be registered\n the token will be acquired in the 'FinishSignup' step.",
            "properties": {
              "accessToken": {
                "type": "string"
              },
              "created": {
                "format": "int64",
                "type": "number"
              },
              "expiry": {
                "format": "int64",
                "type": "number"
              },
              "refreshToken": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "customerID": {
            "description": "Payment provider custommer id that can be used to\n acquire a payment method, see 'micro login' flow for more.\n @todo this is likely not needed",
            "type": "string"
          },
          "message": {
            "description": "Message to display to the user",
            "type": "string"
          },
          "namespace": {
            "description": "Namespace to use\n @todod deprecated since we no longer support OTP logins",
            "type": "string"
          },
          "namespaces": {
            "description": "Namespaces one has access to based on previous invites\n Currently only 1 is supported",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "paymentRequired": {
            "description": "Whether payment is required or not",
            "type": "boolean"
          }
        },
        "title": "VerifyResponse",
        "type": "object"
      }
    },
    "securitySchemes": {
      "MicroAPIToken": {
        "bearerFormat": "JWT",
        "description": "Micro API token",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Generated by protoc-gen-openapi",
    "title": "Go.Micro.Generator.Test",
    "version": "1",
    "x-logo": {
      "altText": "Micro logo",
      "backgroundColor": "#FFFFFF",
      "url": "https://micro.mu/images/brand.png"
    }
  },
  "openapi": "3.0.0",
  "paths": {
    "/test/Signup/CompleteSignup": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupCompleteSignupRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupCompleteSignupResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.CompleteSignup(CompleteSignupRequest)"
      }
    },
    "/test/Signup/HasPaymentMethod": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupHasPaymentMethodRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupHasPaymentMethodResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.HasPaymentMethod(HasPaymentMethodRequest)"
      }
    },
    "/test/Signup/Recover": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupRecoverRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupRecoverResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.Recover(RecoverRequest)"
      }
    },
    "/test/Signup/SendVerificationEmail": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupSendVerificationEmailRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupSendVerificationEmailResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.SendVerificationEmail(SendVerificationEmailRequest)"
      }
    },
    "/test/Signup/SetPaymentMethod": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupSetPaymentMethodRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupSetPaymentMethodResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.SetPaymentMethod(SetPaymentMethodRequest)"
      }
    },
    "/test/Signup/Verify": {
      "parameters": [
        {
          "in": "header",
          "name": "Micro-Namespace",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "requestBody": {
          "$ref": "#/components/requestBodies/SignupVerifyRequest"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/SignupVerifyResponse"
          },
          "default": {
            "$ref": "#/components/responses/MicroAPIError"
          }
        },
        "security": [
          {
            "MicroAPIToken": []
          }
        ],
        "summary": "Signup.Verify(VerifyRequest)"
      }
    }
  },
  "servers": [
    {
      "url": "https://api.m3o.dev",
      "description": "Micro DEV environment"
    },
    {
      "url": "https://api.m3o.com",
      "description": "Micro LIVE environment"
    }
  ]
}`
<|endoftext|>


<commit_before>package simra

import (
	"github.com/pankona/gomo-simra/simra/fps"
	"github.com/pankona/gomo-simra/simra/internal/peer"
)

// Simraer represents an interface of simra instance
type Simraer interface {
	// Start needs to call to enable all function belong to simra package.
	Start(onStart, onStop func())
	// SetScene sets a driver as a scene.
	// If a driver is already set, it is replaced with new one.
	SetScene(driver Driver)
	// AddSprite adds a sprite to current scene with empty texture.
	AddSprite(s Spriter)
	// RemoveSprite removes specified sprite from current scene.
	// Removed sprite will be disappeared.
	RemoveSprite(s Spriter)
	// SetDesiredScreenSize configures virtual screen size.
	// This function must be called at least once before calling Start.
	SetDesiredScreenSize(w, h float32)
	// AddTouchListener registers a listener for notifying touch event.
	// Event is notified when "screen" is touched.
	AddTouchListener(listener peer.TouchListener)
	// RemoveTouchListener unregisters a listener for notifying touch event.
	RemoveTouchListener(listener peer.TouchListener)
	// AddCollisionListener add a callback function that is called on
	// collision is detected between c1 and c2.
	AddCollisionListener(c1, c2 Collider, listener CollisionListener)
	// RemoveAllCollisionListener removes all registered listeners
	RemoveAllCollisionListener()
}

type collisionMap struct {
	c1       Collider
	c2       Collider
	listener CollisionListener
}

// Simra is a struct that provides API interface of simra
type simra struct {
	driver          Driver
	comap           []*collisionMap
	gl              peer.GLer
	spritecontainer peer.SpriteContainerer
	onStart         func()
	onStop          func()
}

// TODO: don't declare as package global.
var sim = &simra{
	comap: make([]*collisionMap, 0),
}

// GetInstance returns instance of Simra.
// It is necessary to call this function to get Simra instance
// since Simra is single instance.
func GetInstance() Simraer {
	return sim
}

type point struct {
	x, y int
}

// FIXME:
func (simra *simra) onUpdate() {
	if simra.driver != nil {
		simra.driver.Drive()
	}
	simra.collisionCheckAndNotify()
	simra.gl.Update(simra.spritecontainer)
}

func (simra *simra) onStopped() {
	peer.LogDebug("IN")
	simra.driver = nil
	simra.gl.Finalize()
	peer.LogDebug("OUT")
}

func (simra *simra) onGomoStart(glc *peer.GLContext) {
	simra.gl.Initialize(glc)
	simra.onStart()
}

func (simra *simra) onGomoStop() {
	simra.spritecontainer.Initialize(simra.gl)
	simra.gl.Finalize()
	simra.onStop()
}

// Start needs to call to enable all function belong to simra package.
func (simra *simra) Start(onStart, onStop func()) {
	peer.LogDebug("IN")
	gl := peer.NewGLPeer()
	sc := peer.GetSpriteContainer()
	sc.Initialize(gl)
	simra.gl = gl
	simra.spritecontainer = sc
	simra.onStart = onStart
	simra.onStop = onStop
	gomo := peer.GetGomo()
	gomo.Initialize(simra.onGomoStart, simra.onGomoStop, simra.onUpdate)
	gomo.Start()
	peer.LogDebug("OUT")
}

// SetScene sets a driver as a scene.
// If a driver is already set, it is replaced with new one.
func (simra *simra) SetScene(driver Driver) {
	peer.LogDebug("IN")
	simra.spritecontainer.RemoveSprites()
	simra.gl.Reset()
	simra.spritecontainer.Initialize(simra.gl)
	peer.GetTouchPeer().RemoveAllTouchListeners()
	simra.spritecontainer.RemoveSprites()

	simra.driver = driver
	simra.spritecontainer.Initialize(simra.gl)
	simra.spritecontainer.AddSprite(&peer.Sprite{}, nil, fps.Progress)

	driver.Initialize()
	peer.LogDebug("OUT")
}

// AddSprite adds a sprite to current scene with empty texture.
func (simra *simra) AddSprite(s Spriter) {
	sp := s.(*sprite)
	simra.spritecontainer.AddSprite(&sp.Sprite, nil, nil)
}

// RemoveSprite removes specified sprite from current scene.
// Removed sprite will be disappeared.
func (simra *simra) RemoveSprite(s Spriter) {
	sp := s.(*sprite)
	sp.texture = nil
	simra.spritecontainer.RemoveSprite(&sp.Sprite)
}

// SetDesiredScreenSize configures virtual screen size.
// This function must be called at least once before calling Start.
func (simra *simra) SetDesiredScreenSize(w, h float32) {
	ss := peer.GetScreenSizePeer()
	ss.SetDesiredScreenSize(w, h)
}

// AddTouchListener registers a listener for notifying touch event.
// Event is notified when "screen" is touched.
func (simra *simra) AddTouchListener(listener peer.TouchListener) {
	peer.GetTouchPeer().AddTouchListener(listener)
}

// RemoveTouchListener unregisters a listener for notifying touch event.
func (simra *simra) RemoveTouchListener(listener peer.TouchListener) {
	peer.GetTouchPeer().RemoveTouchListener(listener)
}

// AddCollisionListener add a callback function that is called on
// collision is detected between c1 and c2.
func (simra *simra) AddCollisionListener(c1, c2 Collider, listener CollisionListener) {
	// TODO: exclusive controll
	LogDebug("IN")
	simra.comap = append(simra.comap, &collisionMap{c1, c2, listener})
	LogDebug("OUT")
}

func (simra *simra) removeCollisionMap(c *collisionMap) {
	result := []*collisionMap{}

	for _, v := range simra.comap {
		if c.c1 != v.c1 && c.c2 != v.c2 && v != c {
			result = append(result, v)
		}
	}

	simra.comap = result
}

// RemoveAllCollisionListener removes all registered listeners
func (simra *simra) RemoveAllCollisionListener() {
	LogDebug("IN")
	simra.comap = nil
	LogDebug("OUT")
}

func (simra *simra) collisionCheckAndNotify() {
	//LogDebug("IN")

	// check collision
	for _, v := range simra.comap {
		// TODO: refactor around here...
		x1, y1, w1, h1 := v.c1.GetXYWH()
		x2, y2, w2, h2 := v.c2.GetXYWH()

		p1 := &point{x1 - w1/2, y1 + h1/2}
		p2 := &point{x1 + w1/2, y1 + h1/2}
		p3 := &point{x1 - w1/2, y1 - h1/2}
		p4 := &point{x1 + w1/2, y1 - h1/2}

		if p1.x >= (x2-w2/2) && p1.x <= (x2+w2/2) &&
			p1.y >= (y2-h2/2) && p1.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p2.x >= (x2-w2/2) && p2.x <= (x2+w2/2) &&
			p2.y >= (y2-h2/2) && p2.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p3.x >= (x2-w2/2) && p3.x <= (x2+w2/2) &&
			p3.y >= (y2-h2/2) && p3.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p4.x >= (x2-w2/2) && p4.x <= (x2+w2/2) &&
			p4.y >= (y2-h2/2) && p4.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
	}
	//LogDebug("OUT")
}

// RemoveCollisionListener removes a collision map by specified collider instance.
func (simra *simra) RemoveCollisionListener(c1, c2 Collider) {
	// TODO: exclusive controll
	LogDebug("IN")
	simra.removeCollisionMap(&collisionMap{c1, c2, nil})
	LogDebug("OUT")
}

func (simra *simra) comapLength() int {
	return len(simra.comap)
}

// LogDebug prints logs.
// From simra, just call peer.LogDebug.
// This is disabled at Release Build.
func LogDebug(format string, a ...interface{}) {
	peer.LogDebug(format, a...)
}

// LogError prints logs.
// From simra, just call peer.LogError.
// This is never disabled even for Release build.
func LogError(format string, a ...interface{}) {
	peer.LogError(format, a...)
}
<commit_msg>use short receiver name for simra<commit_after>package simra

import (
	"github.com/pankona/gomo-simra/simra/fps"
	"github.com/pankona/gomo-simra/simra/internal/peer"
)

// Simraer represents an interface of simra instance
type Simraer interface {
	// Start needs to call to enable all function belong to simra package.
	Start(onStart, onStop func())
	// SetScene sets a driver as a scene.
	// If a driver is already set, it is replaced with new one.
	SetScene(driver Driver)
	// AddSprite adds a sprite to current scene with empty texture.
	AddSprite(s Spriter)
	// RemoveSprite removes specified sprite from current scene.
	// Removed sprite will be disappeared.
	RemoveSprite(s Spriter)
	// SetDesiredScreenSize configures virtual screen size.
	// This function must be called at least once before calling Start.
	SetDesiredScreenSize(w, h float32)
	// AddTouchListener registers a listener for notifying touch event.
	// Event is notified when "screen" is touched.
	AddTouchListener(listener peer.TouchListener)
	// RemoveTouchListener unregisters a listener for notifying touch event.
	RemoveTouchListener(listener peer.TouchListener)
	// AddCollisionListener add a callback function that is called on
	// collision is detected between c1 and c2.
	AddCollisionListener(c1, c2 Collider, listener CollisionListener)
	// RemoveAllCollisionListener removes all registered listeners
	RemoveAllCollisionListener()
}

type collisionMap struct {
	c1       Collider
	c2       Collider
	listener CollisionListener
}

// Simra is a struct that provides API interface of simra
type simra struct {
	driver          Driver
	comap           []*collisionMap
	gl              peer.GLer
	spritecontainer peer.SpriteContainerer
	onStart         func()
	onStop          func()
}

// TODO: don't declare as package global.
var sim = &simra{
	comap: make([]*collisionMap, 0),
}

// GetInstance returns instance of Simra.
// It is necessary to call this function to get Simra instance
// since Simra is single instance.
func GetInstance() Simraer {
	return sim
}

type point struct {
	x, y int
}

func (sim *simra) onUpdate() {
	if sim.driver != nil {
		sim.driver.Drive()
	}
	sim.collisionCheckAndNotify()
	sim.gl.Update(sim.spritecontainer)
}

func (sim *simra) onStopped() {
	peer.LogDebug("IN")
	sim.driver = nil
	sim.gl.Finalize()
	peer.LogDebug("OUT")
}

func (sim *simra) onGomoStart(glc *peer.GLContext) {
	sim.gl.Initialize(glc)
	sim.onStart()
}

func (sim *simra) onGomoStop() {
	sim.spritecontainer.Initialize(sim.gl)
	sim.gl.Finalize()
	sim.onStop()
}

// Start needs to call to enable all function belong to simra package.
func (sim *simra) Start(onStart, onStop func()) {
	peer.LogDebug("IN")
	gl := peer.NewGLPeer()
	sc := peer.GetSpriteContainer()
	sc.Initialize(gl)
	sim.gl = gl
	sim.spritecontainer = sc
	sim.onStart = onStart
	sim.onStop = onStop
	gomo := peer.GetGomo()
	gomo.Initialize(sim.onGomoStart, sim.onGomoStop, sim.onUpdate)
	gomo.Start()
	peer.LogDebug("OUT")
}

// SetScene sets a driver as a scene.
// If a driver is already set, it is replaced with new one.
func (sim *simra) SetScene(driver Driver) {
	peer.LogDebug("IN")
	sim.spritecontainer.RemoveSprites()
	sim.gl.Reset()
	sim.spritecontainer.Initialize(sim.gl)
	peer.GetTouchPeer().RemoveAllTouchListeners()
	sim.spritecontainer.RemoveSprites()

	sim.driver = driver
	sim.spritecontainer.Initialize(sim.gl)
	sim.spritecontainer.AddSprite(&peer.Sprite{}, nil, fps.Progress)

	driver.Initialize()
	peer.LogDebug("OUT")
}

// AddSprite adds a sprite to current scene with empty texture.
func (sim *simra) AddSprite(s Spriter) {
	sp := s.(*sprite)
	sim.spritecontainer.AddSprite(&sp.Sprite, nil, nil)
}

// RemoveSprite removes specified sprite from current scene.
// Removed sprite will be disappeared.
func (sim *simra) RemoveSprite(s Spriter) {
	sp := s.(*sprite)
	sp.texture = nil
	sim.spritecontainer.RemoveSprite(&sp.Sprite)
}

// SetDesiredScreenSize configures virtual screen size.
// This function must be called at least once before calling Start.
func (sim *simra) SetDesiredScreenSize(w, h float32) {
	ss := peer.GetScreenSizePeer()
	ss.SetDesiredScreenSize(w, h)
}

// AddTouchListener registers a listener for notifying touch event.
// Event is notified when "screen" is touched.
func (sim *simra) AddTouchListener(listener peer.TouchListener) {
	peer.GetTouchPeer().AddTouchListener(listener)
}

// RemoveTouchListener unregisters a listener for notifying touch event.
func (sim *simra) RemoveTouchListener(listener peer.TouchListener) {
	peer.GetTouchPeer().RemoveTouchListener(listener)
}

// AddCollisionListener add a callback function that is called on
// collision is detected between c1 and c2.
func (sim *simra) AddCollisionListener(c1, c2 Collider, listener CollisionListener) {
	// TODO: exclusive controll
	LogDebug("IN")
	sim.comap = append(sim.comap, &collisionMap{c1, c2, listener})
	LogDebug("OUT")
}

func (sim *simra) removeCollisionMap(c *collisionMap) {
	result := []*collisionMap{}

	for _, v := range sim.comap {
		if c.c1 != v.c1 && c.c2 != v.c2 && v != c {
			result = append(result, v)
		}
	}

	sim.comap = result
}

// RemoveAllCollisionListener removes all registered listeners
func (sim *simra) RemoveAllCollisionListener() {
	LogDebug("IN")
	sim.comap = nil
	LogDebug("OUT")
}

func (sim *simra) collisionCheckAndNotify() {
	//LogDebug("IN")

	// check collision
	for _, v := range sim.comap {
		// TODO: refactor around here...
		x1, y1, w1, h1 := v.c1.GetXYWH()
		x2, y2, w2, h2 := v.c2.GetXYWH()

		p1 := &point{x1 - w1/2, y1 + h1/2}
		p2 := &point{x1 + w1/2, y1 + h1/2}
		p3 := &point{x1 - w1/2, y1 - h1/2}
		p4 := &point{x1 + w1/2, y1 - h1/2}

		if p1.x >= (x2-w2/2) && p1.x <= (x2+w2/2) &&
			p1.y >= (y2-h2/2) && p1.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p2.x >= (x2-w2/2) && p2.x <= (x2+w2/2) &&
			p2.y >= (y2-h2/2) && p2.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p3.x >= (x2-w2/2) && p3.x <= (x2+w2/2) &&
			p3.y >= (y2-h2/2) && p3.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
		if p4.x >= (x2-w2/2) && p4.x <= (x2+w2/2) &&
			p4.y >= (y2-h2/2) && p4.y <= (y2+h2/2) {
			v.listener.OnCollision(v.c1, v.c2)
			return
		}
	}
	//LogDebug("OUT")
}

// RemoveCollisionListener removes a collision map by specified collider instance.
func (sim *simra) RemoveCollisionListener(c1, c2 Collider) {
	// TODO: exclusive controll
	LogDebug("IN")
	sim.removeCollisionMap(&collisionMap{c1, c2, nil})
	LogDebug("OUT")
}

func (sim *simra) comapLength() int {
	return len(sim.comap)
}

// LogDebug prints logs.
// From simra, just call peer.LogDebug.
// This is disabled at Release Build.
func LogDebug(format string, a ...interface{}) {
	peer.LogDebug(format, a...)
}

// LogError prints logs.
// From simra, just call peer.LogError.
// This is never disabled even for Release build.
func LogError(format string, a ...interface{}) {
	peer.LogError(format, a...)
}
<|endoftext|>


<commit_before>package ircclient

import (
	"os"
	"log"
)

type IRCClient struct {
	conn *IRCConn
	// TODO better config format
	conf    map[string]string
	plugins map[string]Plugin
}

func NewIRCClient(hostport, nick, rname, ident string) *IRCClient {
	c := &IRCClient{nil, make(map[string]string), make(map[string]Plugin)}
	c.conf["nick"] = nick
	c.conf["hostport"] = hostport
	c.conf["rname"] = rname
	c.conf["ident"] = ident
	c.RegisterPlugin(&BasicProtocol{})
	return c
}

func (ic *IRCClient) RegisterPlugin(p Plugin) os.Error {
	if _, ok := ic.plugins[p.String()]; ok == true {
		return os.NewError("Plugin already exists")
	}
	p.Register(ic)
	ic.plugins[p.String()] = p
	return nil
}

func (ic *IRCClient) Connect() os.Error {
	ic.conn = NewIRCConn()
	e := ic.conn.Connect(ic.conf["hostport"])
	if e != nil {
		log.Println("Can't connect " + e.String())
		return e
	}
	ic.conn.Output <- "NICK " + ic.conf["nick"] + "\n"
	ic.conn.Output <- "USER " + ic.conf["ident"] + " * Q :" + ic.conf["rname"] + "\n"
	nick := ic.conf["nick"]
	for {
		s := ParseServerLine(<-ic.conn.Input)
		// TODO error handling
		for _, p := range ic.plugins {
			p.ProcessLine(s)
		}
		switch s.Command {
		case "433":
			// Nickname already in use
			nick = nick + "_"
			ic.conn.Output <- "NICK " + nick + "\n"
		case "001":
			// Successfully registered
			return nil
		}
	}
	return nil // Never happens
}

func (ic *IRCClient) InputLoop() {
	for {
		s := ParseServerLine(<-ic.conn.Input)
		// TODO error handling
		for _, p := range ic.plugins {
			p.ProcessLine(s)
		}
	}
}

func (ic *IRCClient) Disconnect(quitmsg string) {
	ic.conn.Output <- "QUIT :" + quitmsg
	ic.conn.Flush()
	ic.conn.Quit() // Shouldn't be needed, as server closes connection
}
<commit_msg>start plugins::ProcessLine in separate goroutine<commit_after>package ircclient

import (
	"os"
	"log"
)

type IRCClient struct {
	conn *IRCConn
	// TODO better config format
	conf    map[string]string
	plugins map[string]Plugin
}

func NewIRCClient(hostport, nick, rname, ident string) *IRCClient {
	c := &IRCClient{nil, make(map[string]string), make(map[string]Plugin)}
	c.conf["nick"] = nick
	c.conf["hostport"] = hostport
	c.conf["rname"] = rname
	c.conf["ident"] = ident
	c.RegisterPlugin(&BasicProtocol{})
	return c
}

func (ic *IRCClient) RegisterPlugin(p Plugin) os.Error {
	if _, ok := ic.plugins[p.String()]; ok == true {
		return os.NewError("Plugin already exists")
	}
	p.Register(ic)
	ic.plugins[p.String()] = p
	return nil
}

func (ic *IRCClient) Connect() os.Error {
	ic.conn = NewIRCConn()
	e := ic.conn.Connect(ic.conf["hostport"])
	if e != nil {
		log.Println("Can't connect " + e.String())
		return e
	}
	ic.conn.Output <- "NICK " + ic.conf["nick"] + "\n"
	ic.conn.Output <- "USER " + ic.conf["ident"] + " * Q :" + ic.conf["rname"] + "\n"
	nick := ic.conf["nick"]
	for {
		s := ParseServerLine(<-ic.conn.Input)
		// TODO error handling
		for _, p := range ic.plugins {
			p.ProcessLine(s)
		}
		switch s.Command {
		case "433":
			// Nickname already in use
			nick = nick + "_"
			ic.conn.Output <- "NICK " + nick + "\n"
		case "001":
			// Successfully registered
			return nil
		}
	}
	return nil // Never happens
}

func (ic *IRCClient) InputLoop() {
	for {
		s := ParseServerLine(<-ic.conn.Input)
		// TODO error handling
		for _, p := range ic.plugins {
			go p.ProcessLine(s)
		}
	}
}

func (ic *IRCClient) Disconnect(quitmsg string) {
	ic.conn.Output <- "QUIT :" + quitmsg
	ic.conn.Flush()
	ic.conn.Quit() // Shouldn't be needed, as server closes connection
}
<|endoftext|>


<commit_before>package services

import (
	"errors"
	"fmt"
	"github.com/stitchfix/flotilla-os/log"
	"time"

	"github.com/stitchfix/flotilla-os/clients/cluster"
	"github.com/stitchfix/flotilla-os/clients/registry"
	"github.com/stitchfix/flotilla-os/config"
	"github.com/stitchfix/flotilla-os/exceptions"
	"github.com/stitchfix/flotilla-os/execution/engine"
	"github.com/stitchfix/flotilla-os/state"
)

//
// ExecutionService interacts with the state manager and queue manager to queue runs, and perform
// CRUD operations on them
// * Acts as an intermediary layer between state and the execution engine
//
type ExecutionService interface {
	Create(definitionID string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error)
	CreateByAlias(alias string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error)
	List(
		limit int,
		offset int,
		sortOrder string,
		sortField string,
		filters map[string][]string,
		envFilters map[string]string) (state.RunList, error)
	Get(runID string) (state.Run, error)
	UpdateStatus(runID string, status string, exitCode *int64) error
	Terminate(runID string) error
	ReservedVariables() []string
	ListClusters() ([]string, error)
	GetEvents(run state.Run) (state.RunEventList, error)
}

type executionService struct {
	stateManager       state.Manager
	ecsClusterClient   cluster.Client
	eksClusterClient   cluster.Client
	registryClient     registry.Client
	ecsExecutionEngine engine.Engine
	eksExecutionEngine engine.Engine
	reservedEnv        map[string]func(run state.Run) string
}

func (es *executionService) GetEvents(run state.Run) (state.RunEventList, error) {
	return es.eksExecutionEngine.GetEvents(run)
}

//
// NewExecutionService configures and returns an ExecutionService
//
func NewExecutionService(conf config.Config,
	ecsExecutionEngine engine.Engine,
	eksExecutionEngine engine.Engine,
	sm state.Manager,
	ecsClusterClient cluster.Client,
	eksClusterClient cluster.Client,
	rc registry.Client,
	log log.Logger) (ExecutionService, error) {
	es := executionService{
		stateManager:       sm,
		ecsClusterClient:   ecsClusterClient,
		eksClusterClient:   eksClusterClient,
		registryClient:     rc,
		ecsExecutionEngine: ecsExecutionEngine,
		eksExecutionEngine: eksExecutionEngine,
	}
	//
	// Reserved environment variables dynamically generated
	// per run

	ownerKey := conf.GetString("owner_id_var")
	if len(ownerKey) == 0 {
		ownerKey = "FLOTILLA_RUN_OWNER_ID"
	}
	es.reservedEnv = map[string]func(run state.Run) string{
		"FLOTILLA_SERVER_MODE": func(run state.Run) string {
			return conf.GetString("flotilla_mode")
		},
		"FLOTILLA_RUN_ID": func(run state.Run) string {
			return run.RunID
		},
		ownerKey: func(run state.Run) string {
			return run.User
		},
	}
	// Warm cached cluster list
	es.ecsClusterClient.ListClusters()
	return &es, nil
}

//
// ReservedVariables returns the list of reserved run environment variable
// names
//
func (es *executionService) ReservedVariables() []string {
	var keys []string
	for k := range es.reservedEnv {
		keys = append(keys, k)
	}
	return keys
}

//
// Create constructs and queues a new Run on the cluster specified
//
func (es *executionService) Create(definitionID string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error) {

	// Ensure definition exists
	definition, err := es.stateManager.GetDefinition(definitionID)
	if err != nil {
		return state.Run{}, err
	}

	if engine == nil {
		engine = &state.DefaultEngine
	}

	return es.createFromDefinition(definition, clusterName, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
}

//
// Create constructs and queues a new Run on the cluster specified, based on an alias
//
func (es *executionService) CreateByAlias(alias string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error) {

	// Ensure definition exists
	definition, err := es.stateManager.GetDefinitionByAlias(alias)
	if err != nil {
		return state.Run{}, err
	}

	if engine == nil {
		engine = &state.DefaultEngine
	}

	return es.createFromDefinition(definition, clusterName, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
}

func (es *executionService) createFromDefinition(definition state.Definition, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, nodeLifecycle *string, ephemeralStorage *int64) (state.Run, error) {
	var (
		run state.Run
		err error
	)

	// Validate that definition can be run (image exists, cluster has resources)
	if err = es.canBeRun(clusterName, definition, env, *engine); err != nil {
		return run, err
	}

	// Construct run object with StatusQueued and new UUID4 run id
	run, err = es.constructRun(clusterName, definition, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
	if err != nil {
		return run, err
	}

	// Save run to source of state - it is *CRITICAL* to do this
	// -before- queuing to avoid processing unsaved runs
	if err = es.stateManager.CreateRun(run); err != nil {
		return run, err
	}

	// ECS Queue run
	if *engine == state.ECSEngine {
		err = es.ecsExecutionEngine.Enqueue(run)
	}
	if *engine == state.EKSEngine {
		err = es.eksExecutionEngine.Enqueue(run)
	}

	queuedAt := time.Now()

	if err != nil {
		return run, err
	}

	// UpdateStatus the run's QueuedAt field
	if run, err = es.stateManager.UpdateRun(run.RunID, state.Run{QueuedAt: &queuedAt}); err != nil {
		return run, err
	}

	return run, nil
}

func (es *executionService) constructRun(clusterName string, definition state.Definition, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, nodeLifecycle *string, ephemeralStorage *int64) (state.Run, error) {

	var (
		run state.Run
		err error
	)

	if engine == nil {
		engine = &state.DefaultEngine
	}

	runID, err := state.NewRunID(engine)
	if err != nil {
		return run, err
	}

	run = state.Run{
		RunID:            runID,
		ClusterName:      clusterName,
		GroupName:        definition.GroupName,
		DefinitionID:     definition.DefinitionID,
		Alias:            definition.Alias,
		Image:            definition.Image,
		Status:           state.StatusQueued,
		User:             ownerID,
		Command:          command,
		Memory:           memory,
		Cpu:              cpu,
		Gpu:              definition.Gpu,
		Engine:           engine,
		NodeLifecycle:    nodeLifecycle,
		EphemeralStorage: ephemeralStorage,
	}

	runEnv := es.constructEnviron(run, env)
	run.Env = &runEnv
	return run, nil
}

func (es *executionService) constructEnviron(run state.Run, env *state.EnvList) state.EnvList {
	size := len(es.reservedEnv)
	if env != nil {
		size += len(*env)
	}
	runEnv := make([]state.EnvVar, size)
	i := 0
	for k, f := range es.reservedEnv {
		runEnv[i] = state.EnvVar{
			Name:  k,
			Value: f(run),
		}
		i++
	}
	if env != nil {
		for j, e := range *env {
			runEnv[i+j] = e
		}
	}
	return state.EnvList(runEnv)
}

func (es *executionService) canBeRun(clusterName string, definition state.Definition, env *state.EnvList, engine string) error {
	if env != nil {
		for _, e := range *env {
			_, usingRestricted := es.reservedEnv[e.Name]
			if usingRestricted {
				return exceptions.ConflictingResource{
					ErrorString: fmt.Sprintf("environment variable %s is reserved", e.Name)}
			}
		}
	}

	ok, err := es.registryClient.IsImageValid(definition.Image)
	if err != nil {
		return err
	}
	if !ok {
		return exceptions.MissingResource{
			ErrorString: fmt.Sprintf(
				"image [%s] was not found in any of the configured repositories", definition.Image)}
	}

	if engine == state.ECSEngine {
		ok, err = es.ecsClusterClient.CanBeRun(clusterName, definition)
	}
	if engine == state.EKSEngine {
		if *definition.Privileged == true {
			ok, err = false, errors.New("eks cannot run containers with privileged mode")
		} else {
			ok, err = true, nil
		}
	}

	if err != nil {
		return err
	}
	if !ok {
		return exceptions.MalformedInput{
			ErrorString: fmt.Sprintf(
				"definition [%s] cannot be run on cluster [%s]", definition.DefinitionID, clusterName)}
	}
	return nil
}

//
// List returns a list of Runs
// * validates definition_id and status filters
//
func (es *executionService) List(
	limit int,
	offset int,
	sortOrder string,
	sortField string,
	filters map[string][]string,
	envFilters map[string]string) (state.RunList, error) {

	// If definition_id is present in filters, validate its
	// existence first
	definitionID, ok := filters["definition_id"]
	if ok {
		_, err := es.stateManager.GetDefinition(definitionID[0])
		if err != nil {
			return state.RunList{}, err
		}
	}

	if statusFilters, ok := filters["status"]; ok {
		for _, status := range statusFilters {
			if !state.IsValidStatus(status) {
				// Status filter is invalid
				err := exceptions.MalformedInput{
					ErrorString: fmt.Sprintf("invalid status [%s]", status)}
				return state.RunList{}, err
			}
		}
	}
	return es.stateManager.ListRuns(limit, offset, sortField, sortOrder, filters, envFilters, []string{state.ECSEngine, state.EKSEngine})
}

//
// Get returns the run with the given runID
//
func (es *executionService) Get(runID string) (state.Run, error) {
	return es.stateManager.GetRun(runID)
}

//
// UpdateStatus is for supporting some legacy runs that still manually update their status
//
func (es *executionService) UpdateStatus(runID string, status string, exitCode *int64) error {
	if !state.IsValidStatus(status) {
		return exceptions.MalformedInput{ErrorString: fmt.Sprintf("status %s is invalid", status)}
	}
	_, err := es.stateManager.UpdateRun(runID, state.Run{Status: status, ExitCode: exitCode})
	return err
}

//
// Terminate stops the run with the given runID
//
func (es *executionService) Terminate(runID string) error {
	run, err := es.stateManager.GetRun(runID)
	if err != nil {
		return err
	}

	if run.Engine == nil {
		run.Engine = &state.ECSEngine
	}

	// If it's queued and not submitted, set status to stopped (checked by submit worker)
	if run.Status == state.StatusQueued {
		_, err = es.stateManager.UpdateRun(runID, state.Run{Status: state.StatusStopped})
		return err
	}

	if *run.Engine == state.ECSEngine {
		// If it's been submitted, let the status update workers handle setting it to stopped
		if run.Status != state.StatusStopped && len(run.TaskArn) > 0 && len(run.ClusterName) > 0 {
			return es.ecsExecutionEngine.Terminate(run)
		}
	}

	if *run.Engine == state.EKSEngine && run.Status != state.StatusStopped {
		err = es.eksExecutionEngine.Terminate(run)
		if err == nil {
			finishedAt := time.Now()
			_, err = es.stateManager.UpdateRun(run.RunID, state.Run{
				Status:     state.StatusStopped,
				FinishedAt: &finishedAt,
			})
			return err
		}
		return nil
	}

	return exceptions.MalformedInput{
		ErrorString: fmt.Sprintf(
			"invalid run, state: %s, arn: %s, clusterName: %s", run.Status, run.TaskArn, run.ClusterName)}
}

//
// ListClusters returns a list of all execution clusters available
//
func (es *executionService) ListClusters() ([]string, error) {
	return es.ecsClusterClient.ListClusters()
}
<commit_msg>Override ECS traffic to EKS (#189)<commit_after>package services

import (
	"errors"
	"fmt"
	"github.com/stitchfix/flotilla-os/log"
	"math/rand"
	"time"

	"github.com/stitchfix/flotilla-os/clients/cluster"
	"github.com/stitchfix/flotilla-os/clients/registry"
	"github.com/stitchfix/flotilla-os/config"
	"github.com/stitchfix/flotilla-os/exceptions"
	"github.com/stitchfix/flotilla-os/execution/engine"
	"github.com/stitchfix/flotilla-os/state"
)

//
// ExecutionService interacts with the state manager and queue manager to queue runs, and perform
// CRUD operations on them
// * Acts as an intermediary layer between state and the execution engine
//
type ExecutionService interface {
	Create(definitionID string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error)
	CreateByAlias(alias string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error)
	List(
		limit int,
		offset int,
		sortOrder string,
		sortField string,
		filters map[string][]string,
		envFilters map[string]string) (state.RunList, error)
	Get(runID string) (state.Run, error)
	UpdateStatus(runID string, status string, exitCode *int64) error
	Terminate(runID string) error
	ReservedVariables() []string
	ListClusters() ([]string, error)
	GetEvents(run state.Run) (state.RunEventList, error)
}

type executionService struct {
	stateManager       state.Manager
	ecsClusterClient   cluster.Client
	eksClusterClient   cluster.Client
	registryClient     registry.Client
	ecsExecutionEngine engine.Engine
	eksExecutionEngine engine.Engine
	reservedEnv        map[string]func(run state.Run) string
	eksClusterOverride string
	eksOverridePercent int
}

func (es *executionService) GetEvents(run state.Run) (state.RunEventList, error) {
	return es.eksExecutionEngine.GetEvents(run)
}

//
// NewExecutionService configures and returns an ExecutionService
//
func NewExecutionService(conf config.Config,
	ecsExecutionEngine engine.Engine,
	eksExecutionEngine engine.Engine,
	sm state.Manager,
	ecsClusterClient cluster.Client,
	eksClusterClient cluster.Client,
	rc registry.Client,
	log log.Logger) (ExecutionService, error) {
	es := executionService{
		stateManager:       sm,
		ecsClusterClient:   ecsClusterClient,
		eksClusterClient:   eksClusterClient,
		registryClient:     rc,
		ecsExecutionEngine: ecsExecutionEngine,
		eksExecutionEngine: eksExecutionEngine,
	}
	//
	// Reserved environment variables dynamically generated
	// per run

	ownerKey := conf.GetString("owner_id_var")
	if len(ownerKey) == 0 {
		ownerKey = "FLOTILLA_RUN_OWNER_ID"
	}

	es.eksClusterOverride = conf.GetString("eks.cluster_override")
	es.eksOverridePercent = conf.GetInt("eks.cluster_override_percent")

	es.reservedEnv = map[string]func(run state.Run) string{
		"FLOTILLA_SERVER_MODE": func(run state.Run) string {
			return conf.GetString("flotilla_mode")
		},
		"FLOTILLA_RUN_ID": func(run state.Run) string {
			return run.RunID
		},
		ownerKey: func(run state.Run) string {
			return run.User
		},
	}
	// Warm cached cluster list
	_, _ = es.ecsClusterClient.ListClusters()
	return &es, nil
}

//
// ReservedVariables returns the list of reserved run environment variable
// names
//
func (es *executionService) ReservedVariables() []string {
	var keys []string
	for k := range es.reservedEnv {
		keys = append(keys, k)
	}
	return keys
}

//
// Create constructs and queues a new Run on the cluster specified
//
func (es *executionService) Create(definitionID string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error) {

	// Ensure definition exists
	definition, err := es.stateManager.GetDefinition(definitionID)
	if err != nil {
		return state.Run{}, err
	}

	if engine == nil {
		engine = &state.DefaultEngine
	}

	return es.createFromDefinition(definition, clusterName, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
}

//
// Create constructs and queues a new Run on the cluster specified, based on an alias
//
func (es *executionService) CreateByAlias(alias string, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, ephemeralStorage *int64, nodeLifecycle *string) (state.Run, error) {

	// Ensure definition exists
	definition, err := es.stateManager.GetDefinitionByAlias(alias)
	if err != nil {
		return state.Run{}, err
	}

	if engine == nil {
		engine = &state.DefaultEngine
	}

	// Added to facilitate migration of ECS jobs to EKS.
	if engine != &state.EKSEngine && es.eksOverridePercent > 0 && *definition.Privileged == false {
		modulo := 100 / es.eksOverridePercent
		if rand.Int()%modulo == 0 {
			clusterName = es.eksClusterOverride
			engine = &state.EKSEngine
		}
	}

	return es.createFromDefinition(definition, clusterName, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
}

func (es *executionService) createFromDefinition(definition state.Definition, clusterName string, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, nodeLifecycle *string, ephemeralStorage *int64) (state.Run, error) {
	var (
		run state.Run
		err error
	)

	// Validate that definition can be run (image exists, cluster has resources)
	if err = es.canBeRun(clusterName, definition, env, *engine); err != nil {
		return run, err
	}

	// Construct run object with StatusQueued and new UUID4 run id
	run, err = es.constructRun(clusterName, definition, env, ownerID, command, memory, cpu, engine, nodeLifecycle, ephemeralStorage)
	if err != nil {
		return run, err
	}

	// Save run to source of state - it is *CRITICAL* to do this
	// -before- queuing to avoid processing unsaved runs
	if err = es.stateManager.CreateRun(run); err != nil {
		return run, err
	}

	// ECS Queue run
	if *engine == state.ECSEngine {
		err = es.ecsExecutionEngine.Enqueue(run)
	}
	if *engine == state.EKSEngine {
		err = es.eksExecutionEngine.Enqueue(run)
	}

	queuedAt := time.Now()

	if err != nil {
		return run, err
	}

	// UpdateStatus the run's QueuedAt field
	if run, err = es.stateManager.UpdateRun(run.RunID, state.Run{QueuedAt: &queuedAt}); err != nil {
		return run, err
	}

	return run, nil
}

func (es *executionService) constructRun(clusterName string, definition state.Definition, env *state.EnvList, ownerID string, command *string, memory *int64, cpu *int64, engine *string, nodeLifecycle *string, ephemeralStorage *int64) (state.Run, error) {

	var (
		run state.Run
		err error
	)

	if engine == nil {
		engine = &state.DefaultEngine
	}

	runID, err := state.NewRunID(engine)
	if err != nil {
		return run, err
	}

	run = state.Run{
		RunID:            runID,
		ClusterName:      clusterName,
		GroupName:        definition.GroupName,
		DefinitionID:     definition.DefinitionID,
		Alias:            definition.Alias,
		Image:            definition.Image,
		Status:           state.StatusQueued,
		User:             ownerID,
		Command:          command,
		Memory:           memory,
		Cpu:              cpu,
		Gpu:              definition.Gpu,
		Engine:           engine,
		NodeLifecycle:    nodeLifecycle,
		EphemeralStorage: ephemeralStorage,
	}

	runEnv := es.constructEnviron(run, env)
	run.Env = &runEnv
	return run, nil
}

func (es *executionService) constructEnviron(run state.Run, env *state.EnvList) state.EnvList {
	size := len(es.reservedEnv)
	if env != nil {
		size += len(*env)
	}
	runEnv := make([]state.EnvVar, size)
	i := 0
	for k, f := range es.reservedEnv {
		runEnv[i] = state.EnvVar{
			Name:  k,
			Value: f(run),
		}
		i++
	}
	if env != nil {
		for j, e := range *env {
			runEnv[i+j] = e
		}
	}
	return state.EnvList(runEnv)
}

func (es *executionService) canBeRun(clusterName string, definition state.Definition, env *state.EnvList, engine string) error {
	if env != nil {
		for _, e := range *env {
			_, usingRestricted := es.reservedEnv[e.Name]
			if usingRestricted {
				return exceptions.ConflictingResource{
					ErrorString: fmt.Sprintf("environment variable %s is reserved", e.Name)}
			}
		}
	}

	ok, err := es.registryClient.IsImageValid(definition.Image)
	if err != nil {
		return err
	}
	if !ok {
		return exceptions.MissingResource{
			ErrorString: fmt.Sprintf(
				"image [%s] was not found in any of the configured repositories", definition.Image)}
	}

	if engine == state.ECSEngine {
		ok, err = es.ecsClusterClient.CanBeRun(clusterName, definition)
	}
	if engine == state.EKSEngine {
		if *definition.Privileged == true {
			ok, err = false, errors.New("eks cannot run containers with privileged mode")
		} else {
			ok, err = true, nil
		}
	}

	if err != nil {
		return err
	}
	if !ok {
		return exceptions.MalformedInput{
			ErrorString: fmt.Sprintf(
				"definition [%s] cannot be run on cluster [%s]", definition.DefinitionID, clusterName)}
	}
	return nil
}

//
// List returns a list of Runs
// * validates definition_id and status filters
//
func (es *executionService) List(
	limit int,
	offset int,
	sortOrder string,
	sortField string,
	filters map[string][]string,
	envFilters map[string]string) (state.RunList, error) {

	// If definition_id is present in filters, validate its
	// existence first
	definitionID, ok := filters["definition_id"]
	if ok {
		_, err := es.stateManager.GetDefinition(definitionID[0])
		if err != nil {
			return state.RunList{}, err
		}
	}

	if statusFilters, ok := filters["status"]; ok {
		for _, status := range statusFilters {
			if !state.IsValidStatus(status) {
				// Status filter is invalid
				err := exceptions.MalformedInput{
					ErrorString: fmt.Sprintf("invalid status [%s]", status)}
				return state.RunList{}, err
			}
		}
	}
	return es.stateManager.ListRuns(limit, offset, sortField, sortOrder, filters, envFilters, []string{state.ECSEngine, state.EKSEngine})
}

//
// Get returns the run with the given runID
//
func (es *executionService) Get(runID string) (state.Run, error) {
	return es.stateManager.GetRun(runID)
}

//
// UpdateStatus is for supporting some legacy runs that still manually update their status
//
func (es *executionService) UpdateStatus(runID string, status string, exitCode *int64) error {
	if !state.IsValidStatus(status) {
		return exceptions.MalformedInput{ErrorString: fmt.Sprintf("status %s is invalid", status)}
	}
	_, err := es.stateManager.UpdateRun(runID, state.Run{Status: status, ExitCode: exitCode})
	return err
}

//
// Terminate stops the run with the given runID
//
func (es *executionService) Terminate(runID string) error {
	run, err := es.stateManager.GetRun(runID)
	if err != nil {
		return err
	}

	if run.Engine == nil {
		run.Engine = &state.ECSEngine
	}

	// If it's queued and not submitted, set status to stopped (checked by submit worker)
	if run.Status == state.StatusQueued {
		_, err = es.stateManager.UpdateRun(runID, state.Run{Status: state.StatusStopped})
		return err
	}

	if *run.Engine == state.ECSEngine {
		// If it's been submitted, let the status update workers handle setting it to stopped
		if run.Status != state.StatusStopped && len(run.TaskArn) > 0 && len(run.ClusterName) > 0 {
			return es.ecsExecutionEngine.Terminate(run)
		}
	}

	if *run.Engine == state.EKSEngine && run.Status != state.StatusStopped {
		err = es.eksExecutionEngine.Terminate(run)
		if err == nil {
			finishedAt := time.Now()
			_, err = es.stateManager.UpdateRun(run.RunID, state.Run{
				Status:     state.StatusStopped,
				FinishedAt: &finishedAt,
			})
			return err
		}
		return nil
	}

	return exceptions.MalformedInput{
		ErrorString: fmt.Sprintf(
			"invalid run, state: %s, arn: %s, clusterName: %s", run.Status, run.TaskArn, run.ClusterName)}
}

//
// ListClusters returns a list of all execution clusters available
//
func (es *executionService) ListClusters() ([]string, error) {
	return es.ecsClusterClient.ListClusters()
}
<|endoftext|>


<commit_before>// Author: Emmanuel Odeke <odeke@ualberta.ca>

package itinerary

import (
    "time"
)

type Itinerary struct {
    meta interface{}
    expiryNanos int64
    arrivalNanos int64
    origin interface{}
    destination interface{}
}

func (self *Itinerary) Init(ttl int64, origin, dst, meta interface{}) *Itinerary {
    self.meta = meta
    self.origin = origin
    self.arrivalNanos = time.Now().UnixNano()
    self.SetTTLNano(ttl)
    self.destination = dst

    return self
}

func (self *Itinerary) SetTTLNano(ttl int64) {
    self.expiryNanos = self.arrivalNanos + ttl
}

func New(ttl int64, origin, dst, meta interface{}) *Itinerary {
    return new(Itinerary).Init(ttl, origin, dst, meta)
}

func (self *Itinerary) IsExpired() bool {
    return self.expiryNanos < int64(time.Now().UnixNano())
}

func (self *Itinerary) SetOrigin(newOrigin interface{}) {
    self.origin = newOrigin
}

func (self *Itinerary) GetOrigin() interface{} {
    return self.origin
}

func (self *Itinerary) SetDestination(newDestination interface{}) {
    self.destination = newDestination
}

func (self *Itinerary) GetDestination() interface{} {
    return self.destination
}

func (self *Itinerary) GetMeta() interface{} {
    return self.meta
}

func (self *Itinerary) GetExpiry() int64 {
    return self.expiryNanos
}

func (self *Itinerary) GetArrival() int64 {
    return self.arrivalNanos
}

func (self *Itinerary) SetMeta(newMeta interface{}) {
    self.meta = newMeta
}

func (self *Itinerary) LessByExpiry(other *Itinerary) bool {
    return self.GetExpiry() < other.GetExpiry()
}

func (self *Itinerary) LessByArrival(other *Itinerary) bool {
    return self.GetArrival() < other.GetArrival()
}
<commit_msg>Using uint64 to avoid cheats using negatives with ttl<commit_after>// Author: Emmanuel Odeke <odeke@ualberta.ca>

package itinerary

import (
    "time"
)

type Itinerary struct {
    meta interface{}
    expiryNanos uint64
    arrivalNanos uint64
    origin interface{}
    destination interface{}
}

func (self *Itinerary) Init(ttl uint64, origin, dst, meta interface{}) *Itinerary {
    self.meta = meta
    self.origin = origin
    self.arrivalNanos = uint64(time.Now().UnixNano())
    self.SetTTLNano(ttl)
    self.destination = dst

    return self
}

func (self *Itinerary) SetTTLNano(ttl uint64) {
    self.expiryNanos = self.arrivalNanos + ttl
}

func New(ttl uint64, origin, dst, meta interface{}) *Itinerary {
    return new(Itinerary).Init(ttl, origin, dst, meta)
}

func (self *Itinerary) IsExpired() bool {
    return self.expiryNanos < uint64(time.Now().UnixNano())
}

func (self *Itinerary) SetOrigin(newOrigin interface{}) {
    self.origin = newOrigin
}

func (self *Itinerary) GetOrigin() interface{} {
    return self.origin
}

func (self *Itinerary) SetDestination(newDestination interface{}) {
    self.destination = newDestination
}

func (self *Itinerary) GetDestination() interface{} {
    return self.destination
}

func (self *Itinerary) GetMeta() interface{} {
    return self.meta
}

func (self *Itinerary) GetExpiry() uint64 {
    return self.expiryNanos
}

func (self *Itinerary) GetArrival() uint64 {
    return self.arrivalNanos
}

func (self *Itinerary) SetMeta(newMeta interface{}) {
    self.meta = newMeta
}

func (self *Itinerary) LessByExpiry(other *Itinerary) bool {
    return self.GetExpiry() < other.GetExpiry()
}

func (self *Itinerary) LessByArrival(other *Itinerary) bool {
    return self.GetArrival() < other.GetArrival()
}
<|endoftext|>


<commit_before>package slack

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Client struct {
	WebhookURL      string `json:"webhook_url,omitempty"`
	DefaultChannel  string `json:"channel,omitempty"`
	DefaultUsername string `json:"username,omitempty"`
	DefaultEmoji    string `json:"emoji,omitempty"`
}

func (c *Client) SendInfo(format string, args ...interface{}) error {
	return c.Send(&Notification{
		Text: fmt.Sprintf(format, args...),
	})
}

func (c *Client) SendSuccess(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Error: " + msg,
				Text:     msg,
				Color:    "good",
			},
		},
	})
}

func (c *Client) SendWarning(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Error: " + msg,
				Text:     msg,
				Color:    "warning",
			},
		},
	})
}

func (c *Client) SendError(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Error: " + msg,
				Text:     msg,
				Color:    "danger",
			},
		},
	})
}

func (c *Client) Send(n *Notification) error {
	if n.Channel == "" && c.DefaultChannel != "" {
		n.Channel = c.DefaultChannel
	}

	if n.Username == "" && c.DefaultUsername != "" {
		n.Username = c.DefaultUsername
	}

	if n.Emoji == "" && c.DefaultEmoji != "" {
		n.Emoji = c.DefaultEmoji
	}

	buf, err := json.Marshal(n)
	if err != nil {
		return err
	}

	resp, err := http.Post(c.WebhookURL, "application/json", bytes.NewReader(buf))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	output, _ := ioutil.ReadAll(resp.Body)
	if resp.Status[0] != '2' {
		return fmt.Errorf("slack notify: expected status 2xx, got %s: %q", resp.Status, string(output))
	}

	return nil
}

type Notification struct {
	Text        string       `json:"text"`
	Channel     string       `json:"channel,omitempty"`
	Username    string       `json:"username,omitempty"`
	Emoji       string       `json:"icon_emoji,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

type Attachment struct {
	Fallback string  `json:"fallback"`
	Text     string  `json:"text,omitempty"`
	PreText  string  `json:"pretext,omitempty"`
	Color    string  `json:"color,omitempty"`
	Fields   []Field `json:"fields,omitempty"`
}

type Field struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short,omitempty"`
}
<commit_msg>fixed wrong state indicators<commit_after>package slack

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type Client struct {
	WebhookURL      string `json:"webhook_url,omitempty"`
	DefaultChannel  string `json:"channel,omitempty"`
	DefaultUsername string `json:"username,omitempty"`
	DefaultEmoji    string `json:"emoji,omitempty"`
}

func (c *Client) SendInfo(format string, args ...interface{}) error {
	return c.Send(&Notification{
		Text: fmt.Sprintf(format, args...),
	})
}

func (c *Client) SendSuccess(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Success: " + msg,
				Text:     msg,
				Color:    "good",
			},
		},
	})
}

func (c *Client) SendWarning(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Warning: " + msg,
				Text:     msg,
				Color:    "warning",
			},
		},
	})
}

func (c *Client) SendError(format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	return c.Send(&Notification{
		Attachments: []Attachment{
			{
				Fallback: "Error: " + msg,
				Text:     msg,
				Color:    "danger",
			},
		},
	})
}

func (c *Client) Send(n *Notification) error {
	if n.Channel == "" && c.DefaultChannel != "" {
		n.Channel = c.DefaultChannel
	}

	if n.Username == "" && c.DefaultUsername != "" {
		n.Username = c.DefaultUsername
	}

	if n.Emoji == "" && c.DefaultEmoji != "" {
		n.Emoji = c.DefaultEmoji
	}

	buf, err := json.Marshal(n)
	if err != nil {
		return err
	}

	resp, err := http.Post(c.WebhookURL, "application/json", bytes.NewReader(buf))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	output, _ := ioutil.ReadAll(resp.Body)
	if resp.Status[0] != '2' {
		return fmt.Errorf("slack notify: expected status 2xx, got %s: %q", resp.Status, string(output))
	}

	return nil
}

type Notification struct {
	Text        string       `json:"text"`
	Channel     string       `json:"channel,omitempty"`
	Username    string       `json:"username,omitempty"`
	Emoji       string       `json:"icon_emoji,omitempty"`
	Attachments []Attachment `json:"attachments,omitempty"`
}

type Attachment struct {
	Fallback string  `json:"fallback"`
	Text     string  `json:"text,omitempty"`
	PreText  string  `json:"pretext,omitempty"`
	Color    string  `json:"color,omitempty"`
	Fields   []Field `json:"fields,omitempty"`
}

type Field struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short,omitempty"`
}
<|endoftext|>


<commit_before>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shadowsocks

import (
	"bytes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
)

// payloadSizeMask is the maximum size of payload in bytes.
const payloadSizeMask = 0x3FFF // 16*1024 - 1

// Writer is an io.Writer that also implements io.ReaderFrom to
// allow for piping the data without extra allocations and copies.
type Writer interface {
	io.Writer
	io.ReaderFrom
}

type shadowsocksWriter struct {
	writer   io.Writer
	ssCipher shadowaead.Cipher
	// Wrapper for input that arrives as a slice.
	byteWrapper bytes.Reader
	// These are lazily initialized:
	buf  []byte
	aead cipher.AEAD
	// Index of the next encrypted chunk to write.
	counter []byte
}

// NewShadowsocksWriter creates a Writer that encrypts the given Writer using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksWriter(writer io.Writer, ssCipher shadowaead.Cipher) Writer {
	return &shadowsocksWriter{writer: writer, ssCipher: ssCipher}
}

// init generates a random salt, sets up the AEAD object and writes
// the salt to the inner Writer.
func (sw *shadowsocksWriter) init() (err error) {
	if sw.aead == nil {
		salt := make([]byte, sw.ssCipher.SaltSize())
		if _, err := io.ReadFull(rand.Reader, salt); err != nil {
			return fmt.Errorf("failed to generate salt: %v", err)
		}
		_, err := sw.writer.Write(salt)
		if err != nil {
			return fmt.Errorf("failed to write salt: %v", err)
		}
		sw.aead, err = sw.ssCipher.Encrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sw.counter = make([]byte, sw.aead.NonceSize())
		sw.buf = make([]byte, 2+sw.aead.Overhead()+payloadSizeMask+sw.aead.Overhead())
	}
	return nil
}

// WriteBlock encrypts and writes the input buffer as one signed block.
func (sw *shadowsocksWriter) encryptBlock(ciphertext []byte, plaintext []byte) ([]byte, error) {
	out := sw.aead.Seal(ciphertext, sw.counter, plaintext, nil)
	increment(sw.counter)
	return out, nil
}

func (sw *shadowsocksWriter) Write(p []byte) (int, error) {
	sw.byteWrapper.Reset(p)
	n, err := sw.ReadFrom(&sw.byteWrapper)
	return int(n), err
}

func (sw *shadowsocksWriter) ReadFrom(r io.Reader) (int64, error) {
	if err := sw.init(); err != nil {
		return 0, err
	}
	var written int64
	sizeBuf := sw.buf[:2+sw.aead.Overhead()]
	payloadBuf := sw.buf[len(sizeBuf):]
	for {
		plaintextSize, err := r.Read(payloadBuf[:payloadSizeMask])
		if plaintextSize > 0 {
			binary.BigEndian.PutUint16(sizeBuf, uint16(plaintextSize))
			_, err = sw.encryptBlock(sizeBuf[:0], sizeBuf[:2])
			if err != nil {
				return written, fmt.Errorf("failed to encypt payload size: %v", err)
			}
			_, err := sw.encryptBlock(payloadBuf[:0], payloadBuf[:plaintextSize])
			if err != nil {
				return written, fmt.Errorf("failed to encrypt payload: %v", err)
			}
			payloadSize := plaintextSize + sw.aead.Overhead()
			_, err = sw.writer.Write(sw.buf[:len(sizeBuf)+payloadSize])
			written += int64(plaintextSize)
		}
		if err != nil {
			if err == io.EOF { // ignore EOF as per io.ReaderFrom contract
				return written, nil
			}
			return written, fmt.Errorf("Failed to read payload: %v", err)
		}
	}
}

type shadowsocksReader struct {
	reader   io.Reader
	ssCipher shadowaead.Cipher
	// These are lazily initialized:
	aead cipher.AEAD
	// Index of the next encrypted chunk to read.
	counter []byte
	buf     []byte
}

// Reader is an io.Reader that also implements io.WriterTo to
// allow for piping the data without extra allocations and copies.
type Reader interface {
	io.Reader
	io.WriterTo
}

// NewShadowsocksReader creates a Reader that decrypts the given Reader using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksReader(reader io.Reader, ssCipher shadowaead.Cipher) Reader {
	return &readConverter{
		r: &shadowsocksReader{reader: reader, ssCipher: ssCipher},
	}
}

// init reads the salt from the inner Reader and sets up the AEAD object
func (sr *shadowsocksReader) init() (err error) {
	if sr.aead == nil {
		// For chacha20-poly1305, SaltSize is 32, NonceSize is 12 and Overhead is 16.
		salt := make([]byte, sr.ssCipher.SaltSize())
		if _, err := io.ReadFull(sr.reader, salt); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				err = fmt.Errorf("failed to read salt: %v", err)
			}
			return err
		}
		sr.aead, err = sr.ssCipher.Decrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sr.counter = make([]byte, sr.aead.NonceSize())
		sr.buf = make([]byte, payloadSizeMask+sr.aead.Overhead())
	}
	return nil
}

// readBlock reads and decrypts a single signed block of ciphertext.
// The block must exactly match the size of `buf`, including overhead.
// Returns an error only if the block could not be read.
func (sr *shadowsocksReader) readBlock(buf []byte) error {
	_, err := io.ReadFull(sr.reader, buf)
	if err != nil {
		return err
	}
	_, err = sr.aead.Open(buf[:0], sr.counter, buf, nil)
	increment(sr.counter)
	if err != nil {
		return fmt.Errorf("failed to decrypt: %v", err)
	}
	return nil
}

// ReadChunk returns the next decrypted chunk.  The caller must
// complete its use of the returned buffer before the next call.
func (sr *shadowsocksReader) ReadChunk() ([]byte, error) {
	if err := sr.init(); err != nil {
		return nil, err
	}
	sizeBuf := sr.buf[:2+sr.aead.Overhead()]
	if err := sr.readBlock(sizeBuf); err != nil {
		if err != io.EOF && err != io.ErrUnexpectedEOF {
			err = fmt.Errorf("failed to read payload size: %v", err)
		}
		return nil, err
	}
	size := int(binary.BigEndian.Uint16(sizeBuf) & payloadSizeMask)
	payloadBuf := sr.buf[:size+sr.aead.Overhead()]
	if err := sr.readBlock(payloadBuf); err != nil {
		if err == io.EOF { // EOF is not expected mid-chunk.
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	return payloadBuf[:size], nil
}

// readConverter adapts from shadowsocksReader, with source-controlled
// chunk sizes, to Go-style IO.
type readConverter struct {
	r        *shadowsocksReader
	leftover []byte
}

func (c *readConverter) Read(b []byte) (int, error) {
	if err := c.ensureLeftover(); err != nil {
		return 0, err
	}
	n := copy(b, c.leftover)
	c.leftover = c.leftover[n:]
	return n, nil
}

func (c *readConverter) WriteTo(w io.Writer) (written int64, err error) {
	for {
		if err = c.ensureLeftover(); err != nil {
			if err == io.EOF {
				err = nil
			}
			return written, err
		}
		n, err := w.Write(c.leftover)
		written += int64(n)
		c.leftover = c.leftover[n:]
		if err != nil {
			return written, err
		}
	}
}

// Ensures that c.leftover is nonempty.  If leftover is empty, this method
// waits for incoming data and decrypts it.
// Returns an error only if c.leftover could not be populated.
func (c *readConverter) ensureLeftover() error {
	if len(c.leftover) > 0 {
		return nil
	}
	payload, err := c.r.ReadChunk()
	if err != nil {
		return err
	}
	c.leftover = payload
	return nil
}

// increment little-endian encoded unsigned integer b. Wrap around on overflow.
func increment(b []byte) {
	for i := range b {
		b[i]++
		if b[i] != 0 {
			return
		}
	}
}
<commit_msg>Improve names and comments<commit_after>// Copyright 2018 Jigsaw Operations LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package shadowsocks

import (
	"bytes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"

	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
)

// payloadSizeMask is the maximum size of payload in bytes.
const payloadSizeMask = 0x3FFF // 16*1024 - 1

// Writer is an io.Writer that also implements io.ReaderFrom to
// allow for piping the data without extra allocations and copies.
type Writer interface {
	io.Writer
	io.ReaderFrom
}

type shadowsocksWriter struct {
	writer   io.Writer
	ssCipher shadowaead.Cipher
	// Wrapper for input that arrives as a slice.
	byteWrapper bytes.Reader
	// These are lazily initialized:
	buf  []byte
	aead cipher.AEAD
	// Index of the next encrypted chunk to write.
	counter []byte
}

// NewShadowsocksWriter creates a Writer that encrypts the given Writer using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksWriter(writer io.Writer, ssCipher shadowaead.Cipher) Writer {
	return &shadowsocksWriter{writer: writer, ssCipher: ssCipher}
}

// init generates a random salt, sets up the AEAD object and writes
// the salt to the inner Writer.
func (sw *shadowsocksWriter) init() (err error) {
	if sw.aead == nil {
		salt := make([]byte, sw.ssCipher.SaltSize())
		if _, err := io.ReadFull(rand.Reader, salt); err != nil {
			return fmt.Errorf("failed to generate salt: %v", err)
		}
		_, err := sw.writer.Write(salt)
		if err != nil {
			return fmt.Errorf("failed to write salt: %v", err)
		}
		sw.aead, err = sw.ssCipher.Encrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		sw.counter = make([]byte, sw.aead.NonceSize())
		sw.buf = make([]byte, 2+sw.aead.Overhead()+payloadSizeMask+sw.aead.Overhead())
	}
	return nil
}

// WriteBlock encrypts and writes the input buffer as one signed block.
func (sw *shadowsocksWriter) encryptBlock(ciphertext []byte, plaintext []byte) ([]byte, error) {
	out := sw.aead.Seal(ciphertext, sw.counter, plaintext, nil)
	increment(sw.counter)
	return out, nil
}

func (sw *shadowsocksWriter) Write(p []byte) (int, error) {
	sw.byteWrapper.Reset(p)
	n, err := sw.ReadFrom(&sw.byteWrapper)
	return int(n), err
}

func (sw *shadowsocksWriter) ReadFrom(r io.Reader) (int64, error) {
	if err := sw.init(); err != nil {
		return 0, err
	}
	var written int64
	sizeBuf := sw.buf[:2+sw.aead.Overhead()]
	payloadBuf := sw.buf[len(sizeBuf):]
	for {
		plaintextSize, err := r.Read(payloadBuf[:payloadSizeMask])
		if plaintextSize > 0 {
			binary.BigEndian.PutUint16(sizeBuf, uint16(plaintextSize))
			_, err = sw.encryptBlock(sizeBuf[:0], sizeBuf[:2])
			if err != nil {
				return written, fmt.Errorf("failed to encypt payload size: %v", err)
			}
			_, err := sw.encryptBlock(payloadBuf[:0], payloadBuf[:plaintextSize])
			if err != nil {
				return written, fmt.Errorf("failed to encrypt payload: %v", err)
			}
			payloadSize := plaintextSize + sw.aead.Overhead()
			_, err = sw.writer.Write(sw.buf[:len(sizeBuf)+payloadSize])
			written += int64(plaintextSize)
		}
		if err != nil {
			if err == io.EOF { // ignore EOF as per io.ReaderFrom contract
				return written, nil
			}
			return written, fmt.Errorf("Failed to read payload: %v", err)
		}
	}
}

// ChunkReader is similar to io.Reader, except that it controls its own
// buffer granularity.
type ChunkReader interface {
	// ReadChunk reads the next chunk and returns its payload.  The caller must
	// complete its use of the returned buffer before the next call.
	// The buffer is nil iff there is an error.  io.EOF indicates a close.
	ReadChunk() ([]byte, error)
}

type chunkReader struct {
	reader   io.Reader
	ssCipher shadowaead.Cipher
	// These are lazily initialized:
	aead cipher.AEAD
	// Index of the next encrypted chunk to read.
	counter []byte
	buf     []byte
}

// Reader is an io.Reader that also implements io.WriterTo to
// allow for piping the data without extra allocations and copies.
type Reader interface {
	io.Reader
	io.WriterTo
}

// NewShadowsocksReader creates a Reader that decrypts the given Reader using
// the shadowsocks protocol with the given shadowsocks cipher.
func NewShadowsocksReader(reader io.Reader, ssCipher shadowaead.Cipher) Reader {
	return &readConverter{
		cr: &chunkReader{reader: reader, ssCipher: ssCipher},
	}
}

// init reads the salt from the inner Reader and sets up the AEAD object
func (cr *chunkReader) init() (err error) {
	if cr.aead == nil {
		// For chacha20-poly1305, SaltSize is 32, NonceSize is 12 and Overhead is 16.
		salt := make([]byte, cr.ssCipher.SaltSize())
		if _, err := io.ReadFull(cr.reader, salt); err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				err = fmt.Errorf("failed to read salt: %v", err)
			}
			return err
		}
		cr.aead, err = cr.ssCipher.Decrypter(salt)
		if err != nil {
			return fmt.Errorf("failed to create AEAD: %v", err)
		}
		cr.counter = make([]byte, cr.aead.NonceSize())
		cr.buf = make([]byte, payloadSizeMask+cr.aead.Overhead())
	}
	return nil
}

// readMessage reads, decrypts, and verifies a single AEAD ciphertext.
// The ciphertext and tag (i.e. "overhead") must exactly fill `buf`,
// and the decrypted message will be placed in buf[:len(buf)-overhead].
// Returns an error only if the block could not be read.
func (cr *chunkReader) readMessage(buf []byte) error {
	_, err := io.ReadFull(cr.reader, buf)
	if err != nil {
		return err
	}
	_, err = cr.aead.Open(buf[:0], cr.counter, buf, nil)
	increment(cr.counter)
	if err != nil {
		return fmt.Errorf("failed to decrypt: %v", err)
	}
	return nil
}

func (cr *chunkReader) ReadChunk() ([]byte, error) {
	if err := cr.init(); err != nil {
		return nil, err
	}
	// In Shadowsocks-AEAD, each chunk consists of two
	// encrypted messages.  The first message contains the payload length,
	// and the second message is the payload.
	sizeBuf := cr.buf[:2+cr.aead.Overhead()]
	if err := cr.readMessage(sizeBuf); err != nil {
		if err != io.EOF && err != io.ErrUnexpectedEOF {
			err = fmt.Errorf("failed to read payload size: %v", err)
		}
		return nil, err
	}
	size := int(binary.BigEndian.Uint16(sizeBuf) & payloadSizeMask)
	sizeWithTag := size + cr.aead.Overhead()
	if cap(cr.buf) < sizeWithTag {
		// This code is unreachable.
		return nil, io.ErrShortBuffer
	}
	payloadBuf := cr.buf[:sizeWithTag]
	if err := cr.readMessage(payloadBuf); err != nil {
		if err == io.EOF { // EOF is not expected mid-chunk.
			err = io.ErrUnexpectedEOF
		}
		return nil, err
	}
	return payloadBuf[:size], nil
}

// readConverter adapts from ChunkReader, with source-controlled
// chunk sizes, to Go-style IO.
type readConverter struct {
	cr       ChunkReader
	leftover []byte
}

func (c *readConverter) Read(b []byte) (int, error) {
	if err := c.ensureLeftover(); err != nil {
		return 0, err
	}
	n := copy(b, c.leftover)
	c.leftover = c.leftover[n:]
	return n, nil
}

func (c *readConverter) WriteTo(w io.Writer) (written int64, err error) {
	for {
		if err = c.ensureLeftover(); err != nil {
			if err == io.EOF {
				err = nil
			}
			return written, err
		}
		n, err := w.Write(c.leftover)
		written += int64(n)
		c.leftover = c.leftover[n:]
		if err != nil {
			return written, err
		}
	}
}

// Ensures that c.leftover is nonempty.  If leftover is empty, this method
// waits for incoming data and decrypts it.
// Returns an error only if c.leftover could not be populated.
func (c *readConverter) ensureLeftover() error {
	if len(c.leftover) > 0 {
		return nil
	}
	payload, err := c.cr.ReadChunk()
	if err != nil {
		return err
	}
	c.leftover = payload
	return nil
}

// increment little-endian encoded unsigned integer b. Wrap around on overflow.
func increment(b []byte) {
	for i := range b {
		b[i]++
		if b[i] != 0 {
			return
		}
	}
}
<|endoftext|>