-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add math operation. #8
Conversation
`Attr` acts just like `attr`, but operates on a single `IfAddr` whereas `attr` operates on `IfAttrs`.
`math` takes two arguments: an operation and a string value. The two initial operations are `address` and `network`. The `address` operation's value is a decimal encoded string with a mandatory sign. The value is added or subtracted from the IP address of the IfAddr. Values will overflow or underflow, and if necessary, wrap at the underlying fundamental type. The `network` operation's value is a decimal encoded string with a mandatory sign. If the value is positive it is added to the network address of the IfAddr. If the value overflows the network size, the value is wrapped at the network boundary (i.e. 257 will wrap in a /24 to be the same as +1). If the value is negative it is subtracted from the broadcast address of the IfAddr. If the value underflows the network size, the value will wrap. The `network` operation is intended to address the concern raised in hashicorp#7 but in a more generic way.
This should be useful reference material to future eyeballs from search engines.
The ability to have this kind of IP math within a template is extremely helpful, especially the sorting and maps plus math. Template format can get a little unwieldy (when parsing) but totally forgiving given the derived behavior. 👍 |
nice! Originally I thought about doing this, but
|
@taylorchu That's correct, it doesn't work (
Does that work for you? |
@cheapRoc Yeah, I don't like the Go template language, but I do like that "it just works" and pseudo-standard. I wish it had type support and all arguments didn't need to get passed in as strings, but I digress. There are helper functions along the way that help do the right or sane things. A |
this works perfectly. |
I will be nice if more helper functions work in the space of "IfAddrs/IfAddr" instead of "string" because string is simply not chainable. If we need string, we could use |
@taylorchu This is one of the rubs of
Where the chain is
Which would emit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted a few things.
ifaddrs.go
Outdated
@@ -652,6 +669,171 @@ func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs, | |||
return includedIfs, excludedIfs, nil | |||
} | |||
|
|||
// IfAddrMath will return a new IfAddr struct with a mutaterd value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"mutater"?
ifaddrs.go
Outdated
// underlying type. | ||
|
||
if !signRe.MatchString(value) { | ||
return IfAddr{}, fmt.Errorf("sign (+/-) is required operation %q", operation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"is required for operation"
ifaddrs.go
Outdated
// wrapping is applied. | ||
|
||
if !signRe.MatchString(value) { | ||
return IfAddr{}, fmt.Errorf("sign (+/-) is required operation %q", operation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"required for operation"
template/template.go
Outdated
"math": sockaddr.IfAddrsMath, | ||
|
||
// Misc functions that operate on IfAddr input | ||
"Attr": sockaddr.IfAttr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having Attr
and attr
seems super confusing - is there a better name we can give the new one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chewed on this a bit before settling on mixed-cases. The problem is that attr
accepts an IfAddrs
as its argument, not an IfAddr
.
The original thinking of the template functions was that sources at the start of the template pipeline would be uppercase and everything that operated on IfAddrs
structs would be lowercase to indicate that they can't come first in the pipeline. e.g.
https://github.com/hashicorp/go-sockaddr/blob/master/template/template.go#L60-L71
So far this is the case, but with the math
template operation, I explicitly wanted to avoid what I did with addr
where I did something sketchy on the grounds that it was good UX. The behavior of attr
is such that it only returns the attribute from the first element in IfAddrs
. Originally I did this because I didn't want to write | limit 1 | attr "address"
everywhere (history note: originally the API only had | limit 1 | join "address" ""
and that experience was awful so I conjured up | attr "address"
to extract an individual attribute as a string).
I was already thinking about using interface{}
but I didn't want to sacrifice type safety in the library portion of the go-sockaddr
and this seemed like a slippery slope:
// IfAttrs forwards the selector to IfAttrs.Attr() for resolution. If there the
// argument passed is IfAddrs, only the first IfAddr in the slice is used. If
// the argument is an IfAddr, it will be used.
func IfAttrs(selectorName string, ifAddrsRaw interface{}) (string, error) {
attrName := AttrName(strings.ToLower(selectorName))
switch v := ifAddrsRaw.(type) {
case IfAddrs:
if len(v) == 0 {
return "", nil
}
attrVal, err := v[0].Attr(attrName)
return attrVal, err
case IfAddr:
attrVal, err := v.Attr(attrName)
return attrVal, err
default:
return "", fmt.Errorf("unable to obtain attribute %s from type %T (%v)", selectorName, ifAddrsRaw, ifAddrsRaw)
}
}
... you know what. Let me drop a facade over sockaddr.IfAttr
and sockaddr.IfAddrs
that does the type-unsafe interface{}
type assertions.
I'm not sure why I didn't think of this earlier but I'm going to extend this idiom elsewhere to paper over various user issues (i.e. naked numbers in the template pipeline). LMK what you think now.
* Adds plural IP helpers (e.g. `GetPrivateIPs`, `GetPublicIPs`) hashicorp#11 * Adds subnet math hashicorp#8 * Fixes helper functions for dual-homed hosts hashicorp#10)
* Adds plural IP helpers (e.g. `GetPrivateIPs`, `GetPublicIPs`) hashicorp#11 * Adds subnet math hashicorp#8 * Fixes helper functions for dual-homed hosts hashicorp#10)
Add the
math
operation to perform calculations on IPs and networks. …math
takes two arguments: an operation and a string value. The two initialoperations are
address
andnetwork
.The
address
operation's value is a decimal encoded string with a mandatorysign. The value is added or subtracted from the IP address of the IfAddr.
Values will overflow or underflow, and if necessary, wrap at the underlying
fundamental type.
The
network
operation's value is a decimal encoded string with a mandatorysign. If the value is positive it is added to the network address of the
IfAddr. If the value overflows the network size, the value is wrapped at the
network boundary (i.e. 257 will wrap in a /24 to be the same as +1). If the
value is negative it is subtracted from the broadcast address of the IfAddr.
If the value underflows the network size, the value will wrap.
The
network
operation is intended to address the concern raised in #7 butin a more generic way and with IPv6 support, thereby superseding #7. For instance, to get the DNS resolver, one would simply use:
Thank you @taylorchu for the feature request. Can you let me know if this PR meets your needs? I know it scratches my back in terms of something that I've needed for a while now.