<commit_before>package filters

import (
	"encoding/json"
	"fmt"
	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"regexp"
	"strings"
)

var ec2InstanceIdRegex = regexp.MustCompile("i-[0-9a-f]{8}")

type ec2InstanceIdParser interface {
	Parse(input string) string
}
type realEc2InstanceIdParser struct{}

func (p realEc2InstanceIdParser) Parse(input string) string {
	return ec2InstanceIdRegex.FindString(input)
}

type ec2InstanceIdLookup struct {
	region        string
	commandRunner util.CommandRunner
	idParser      ec2InstanceIdParser
}

func (f *ec2InstanceIdLookup) Filter(targets []target.Target) []target.Target {
	if f.region == "" {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	// TODO: build map by parsed id, do a single query using --instance-ids
	for idx, t := range targets {
		instanceId := f.idParser.Parse(t.Host)
		if len(instanceId) > 0 {
			util.Logger.Infof("EC2 Instance lookup: %s in %s", instanceId, f.region)

			output, err := f.commandRunner.RunGetOutput("aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", f.region})
			util.Logger.Debugf("Response from AWS API: %s", output)
			if err != nil {
				util.Logger.Infof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", t.Host, instanceId, f.region, strings.TrimSpace(string(output)))
				continue
			}

			var data map[string]interface{}
			if err = json.Unmarshal(output, &data); err != nil {
				panic(fmt.Sprintf("Invalid JSON returned by AWS API.\nError: %s\nJSON follows this line\n%s", err.Error(), output))
			}

			var reservations = data["Reservations"]
			if reservations == nil || len(reservations.([]interface{})) == 0 {
				util.Logger.Infof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", t.Host, instanceId, f.region)
				continue
			}
			targets[idx].Host = reservations.([]interface{})[0].(map[string]interface{})["Instances"].([]interface{})[0].(map[string]interface{})["PublicIpAddress"].(string)
		} else {
			util.Logger.Debugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", t, f.region)
		}
	}
	return targets<commit_msg>ec2-instance-id filter: proper JSON unmarshalling<commit_after>package filters

import (
	"encoding/json"
	"fmt"
	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"regexp"
	"strings"
)

var ec2InstanceIdRegex = regexp.MustCompile("i-[0-9a-f]{8}")

type ec2InstanceIdParser interface {
	Parse(input string) string
}
type realEc2InstanceIdParser struct{}

func (p realEc2InstanceIdParser) Parse(input string) string {
	return ec2InstanceIdRegex.FindString(input)
}

type apiResponse struct {
	Reservations []struct {
		Instances []struct {
			PublicDnsName   string
			PublicIpAddress string
		}
	}
}

type ec2InstanceIdLookup struct {
	region        string
	commandRunner util.CommandRunner
	idParser      ec2InstanceIdParser
}

func (f *ec2InstanceIdLookup) Filter(targets []target.Target) []target.Target {
	if f.region == "" {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	// TODO: build map by parsed id, do a single query using --instance-ids
	for idx, t := range targets {
		instanceId := f.idParser.Parse(t.Host)
		if len(instanceId) > 0 {
			util.Logger.Infof("EC2 Instance lookup: %s in %s", instanceId, f.region)

			output, err := f.commandRunner.RunGetOutput("aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", f.region})
			util.Logger.Debugf("Response from AWS API: %s", output)
			if err != nil {
				util.Logger.Infof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", t.Host, instanceId, f.region, strings.TrimSpace(string(output)))
				continue
			}

			var data apiResponse
			if err = json.Unmarshal(output, &data); err != nil {
				panic(fmt.Sprintf("Invalid JSON returned by AWS API.\nError: %s\nJSON follows this line\n%s", err.Error(), output))
			}

			if data.Reservations == nil || len(data.Reservations) == 0 {
				util.Logger.Infof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", t.Host, instanceId, f.region)
				continue
			}
			targets[idx].Host = data.Reservations[0].Instances[0].PublicIpAddress
		} else {
			util.Logger.Debugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", t, f.region)
		}
	}
	return targets
{'fraction_non_alphanumeric': 0.13628434365460096, 'fraction_numerical': 0.009376362843436545, 'mean_word_length': 4.364912280701755, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '1665595', 'n_tokens_mistral': 1610, 'n_tokens_neox': 1477, 'n_words': 411}