<commit_before>package koding

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"koding/db/models"
	"koding/db/mongodb"
	aws "koding/kites/kloud/api/amazon"
	"koding/kites/kloud/klient"
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/protocol"
	"koding/kites/kloud/provider/amazon"

	"github.com/koding/kite"
	"github.com/koding/logging"
	"github.com/mitchellh/goamz/ec2"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// Checker checks various aspects of a machine. It is used for limiting certain
// aspects of a machine, such as the total allowed machine count, storage size
// and etc.
type Checker interface {
	// Total checks whether the user has reached the current plan's limit of
	// having a total number numbers of machines. It returns an error if the
	// limit is reached or an unexplained error happaned.
	Total() error

	// AlwaysOn checks whether the given machine has reached the current plans
	// always on limit
	AlwaysOn() error

	// Timeout checks whether the user has reached the current plan's
	// inactivity timeout.
	Timeout() error

	// Storage checks whether the user has reached the current plan's limit
	// total storage with the supplied wantStorage information. It returns an
	// error if the limit is reached or an unexplained error happaned.
	Storage(wantStorage int) error

	// AllowedInstances checks whether the given machine has the permisison to
	// create the given instance type
	AllowedInstances(wantInstance InstanceType) error

	// NetworkUsage checks whether the given machine has exceeded the network
	// outbound limit
	NetworkUsage() error
}

type PlanChecker struct {
	Api                  *amazon.AmazonClient
	DB                   *mongodb.MongoDB
	Machine              *protocol.Machine
	Provider             *Provider
	Kite                 *kite.Kite
	Username             string
	Log                  logging.Logger
	Plan                 Plan
	NetworkUsageEndpoint string
}

type networkUsageResponse struct {
	CanStart     bool   `json:"canStart"`
	Reason       string `json:"reason"`
	AllowedUsage int    `json:"allowedUsage"`
	CurrentUsage int    `json:"currentUsage"`
}

func (p *PlanChecker) NetworkUsage() error {
	networkEndpoint, err := url.Parse(p.NetworkUsageEndpoint)
	if err != nil {
		p.Log.Debug("[%s] Failed to parse network-usage endpoint: %v. err: %v",
			p.Machine.Id, p.NetworkUsageEndpoint, err)
		return err
	}

	var account *models.Account
	if err := p.DB.Run("jAccounts", func(c *mgo.Collection) error {
		return c.Find(bson.M{"profile.nickname": p.Username}).One(&account)
	}); err != nil {
		p.Log.Warning("[%s] Failed to fetch user information while checking network-usage. err: %v",
			p.Machine.Id, err)
		return err
	}

	q := networkEndpoint.Query()
	q.Set("account_id", account.Id.Hex())
	networkEndpoint.RawQuery = q.Encode()

	resp, err := http.Get(networkEndpoint.String())
	if err != nil {
		p.Log.Warning("[%s] Failed to fetch network-usage because network-usage providing api host seems down. err: %v",
			p.Machine.Id, err)
		return err
	}
	defer resp.Body.Close()

	var usageResponse *networkUsageResponse
	if err := json.NewDecoder(resp.Body).Decode(&usageResponse); err != nil {
		p.Log.Warning("[%s] Failed to decode network-usage response. err: %v",
			p.Machine.Id, err)
		return err
	}

	if resp.StatusCode != 200 {
		p.Log.Debug("[%s] Network-usage response code is not 200. It's %v",
			p.Machine.Id, resp.StatusCode)
		return err
	}

	if !usageResponse.CanStart {
		p.Log.Debug("[%s] Network-usage limit is reached. Allowed usage: %v MiB, Current usage: %v MiB",
			p.Machine.Id, usageResponse.AllowedUsage, usageResponse.CurrentUsage)

		err := fmt.Errorf("%s; allowed: %d, current: %d",
			usageResponse.Reason, usageResponse.AllowedUsage,
			usageResponse.CurrentUsage,
		)

		return err
	}
	return nil
}

func (p *PlanChecker) AllowedInstances(wantInstance InstanceType) error {<commit_msg>kloud: check for network usage request status code before  unmarshalling response<commit_after>package koding

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"koding/db/models"
	"koding/db/mongodb"
	aws "koding/kites/kloud/api/amazon"
	"koding/kites/kloud/klient"
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/protocol"
	"koding/kites/kloud/provider/amazon"

	"github.com/koding/kite"
	"github.com/koding/logging"
	"github.com/mitchellh/goamz/ec2"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// Checker checks various aspects of a machine. It is used for limiting certain
// aspects of a machine, such as the total allowed machine count, storage size
// and etc.
type Checker interface {
	// Total checks whether the user has reached the current plan's limit of
	// having a total number numbers of machines. It returns an error if the
	// limit is reached or an unexplained error happaned.
	Total() error

	// AlwaysOn checks whether the given machine has reached the current plans
	// always on limit
	AlwaysOn() error

	// Timeout checks whether the user has reached the current plan's
	// inactivity timeout.
	Timeout() error

	// Storage checks whether the user has reached the current plan's limit
	// total storage with the supplied wantStorage information. It returns an
	// error if the limit is reached or an unexplained error happaned.
	Storage(wantStorage int) error

	// AllowedInstances checks whether the given machine has the permisison to
	// create the given instance type
	AllowedInstances(wantInstance InstanceType) error

	// NetworkUsage checks whether the given machine has exceeded the network
	// outbound limit
	NetworkUsage() error
}

type PlanChecker struct {
	Api                  *amazon.AmazonClient
	DB                   *mongodb.MongoDB
	Machine              *protocol.Machine
	Provider             *Provider
	Kite                 *kite.Kite
	Username             string
	Log                  logging.Logger
	Plan                 Plan
	NetworkUsageEndpoint string
}

type networkUsageResponse struct {
	CanStart     bool   `json:"canStart"`
	Reason       string `json:"reason"`
	AllowedUsage int    `json:"allowedUsage"`
	CurrentUsage int    `json:"currentUsage"`
}

func (p *PlanChecker) NetworkUsage() error {
	networkEndpoint, err := url.Parse(p.NetworkUsageEndpoint)
	if err != nil {
		p.Log.Debug("[%s] Failed to parse network-usage endpoint: %v. err: %v",
			p.Machine.Id, p.NetworkUsageEndpoint, err)
		return err
	}

	var account *models.Account
	if err := p.DB.Run("jAccounts", func(c *mgo.Collection) error {
		return c.Find(bson.M{"profile.nickname": p.Username}).One(&account)
	}); err != nil {
		p.Log.Warning("[%s] Failed to fetch user information while checking network-usage. err: %v",
			p.Machine.Id, err)
		return err
	}

	q := networkEndpoint.Query()
	q.Set("account_id", account.Id.Hex())
	networkEndpoint.RawQuery = q.Encode()

	resp, err := http.Get(networkEndpoint.String())
	if err != nil {
		p.Log.Warning("[%s] Failed to fetch network-usage because network-usage providing api host seems down. err: %v",
			p.Machine.Id, err)
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		p.Log.Debug("[%s] Network-usage response code is not 200. It's %v",
			p.Machine.Id, resp.StatusCode)
		return err
	}

	var usageResponse *networkUsageResponse
	if err := json.NewDecoder(resp.Body).Decode(&usageResponse); err != nil {
		p.Log.Warning("[%s] Failed to decode network-usage response. err: %v",
			p.Machine.Id, err)
		return err
	}
	if !usageResponse.CanStart {
		p.Log.Debug("[%s] Network-usage limit is reached. Allowed usage: %v MiB, Current usage: %v MiB",
			p.Machine.Id, usageResponse.AllowedUsage, usageResponse.CurrentUsage)

		err := fmt.Errorf("%s; allowed: %d, current: %d",
			usageResponse.Reason, usageResponse.AllowedUsage,
			usageResponse.CurrentUsage,
		)

		return err
	}
	return nil
}

func (p *PlanChecker) AllowedInstances(wantInstance InstanceType) error {
{'fraction_non_alphanumeric': 0.1093414233345974, 'fraction_numerical': 0.0022753128555176336, 'mean_word_length': 3.4827195467422096, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5390689', 'n_tokens_mistral': 2592, 'n_tokens_neox': 2478, 'n_words': 768}