2018-11-14 12:18:03 +03:00
package types
import (
"encoding"
"errors"
"fmt"
"strings"
"github.com/ryanuber/go-glob"
)
// Constraint holds a parsed constraint expression.
2019-06-17 12:48:05 +03:00
// FIXME replace by a string.
2018-11-14 12:18:03 +03:00
type Constraint struct {
2019-06-17 12:48:05 +03:00
Key string ` description:"The provider label that will be matched against. In practice, it is always 'tag'." export:"true" `
2018-11-14 12:18:03 +03:00
// MustMatch is true if operator is "==" or false if operator is "!="
2019-06-17 12:48:05 +03:00
MustMatch bool ` description:"Whether the matching operator is equals or not equals." export:"true" `
Value string ` description:"The value that will be matched against." export:"true" ` // TODO: support regex
2018-11-14 12:18:03 +03:00
}
// NewConstraint receives a string and return a *Constraint, after checking syntax and parsing the constraint expression.
func NewConstraint ( exp string ) ( * Constraint , error ) {
sep := ""
constraint := & Constraint { }
2019-02-05 19:10:03 +03:00
switch {
case strings . Contains ( exp , "==" ) :
2018-11-14 12:18:03 +03:00
sep = "=="
constraint . MustMatch = true
2019-02-05 19:10:03 +03:00
case strings . Contains ( exp , "!=" ) :
2018-11-14 12:18:03 +03:00
sep = "!="
constraint . MustMatch = false
2019-02-05 19:10:03 +03:00
default :
2018-11-14 12:18:03 +03:00
return nil , errors . New ( "constraint expression missing valid operator: '==' or '!='" )
}
kv := strings . SplitN ( exp , sep , 2 )
if len ( kv ) == 2 {
// At the moment, it only supports tags
if kv [ 0 ] != "tag" {
return nil , errors . New ( "constraint must be tag-based. Syntax: tag==us-*" )
}
constraint . Key = kv [ 0 ]
2019-06-17 12:48:05 +03:00
constraint . Value = kv [ 1 ]
2018-11-14 12:18:03 +03:00
return constraint , nil
}
return nil , fmt . Errorf ( "incorrect constraint expression: %s" , exp )
}
func ( c * Constraint ) String ( ) string {
if c . MustMatch {
2019-06-17 12:48:05 +03:00
return c . Key + "==" + c . Value
2018-11-14 12:18:03 +03:00
}
2019-06-17 12:48:05 +03:00
return c . Key + "!=" + c . Value
2018-11-14 12:18:03 +03:00
}
var _ encoding . TextUnmarshaler = ( * Constraint ) ( nil )
// UnmarshalText defines how unmarshal in TOML parsing
func ( c * Constraint ) UnmarshalText ( text [ ] byte ) error {
constraint , err := NewConstraint ( string ( text ) )
if err != nil {
return err
}
c . Key = constraint . Key
c . MustMatch = constraint . MustMatch
2019-06-17 12:48:05 +03:00
c . Value = constraint . Value
2018-11-14 12:18:03 +03:00
return nil
}
var _ encoding . TextMarshaler = ( * Constraint ) ( nil )
// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
func ( c * Constraint ) MarshalText ( ) ( text [ ] byte , err error ) {
return [ ] byte ( c . String ( ) ) , nil
}
// MatchConstraintWithAtLeastOneTag tests a constraint for one single service.
func ( c * Constraint ) MatchConstraintWithAtLeastOneTag ( tags [ ] string ) bool {
for _ , tag := range tags {
2019-06-17 12:48:05 +03:00
if glob . Glob ( c . Value , tag ) {
2018-11-14 12:18:03 +03:00
return true
}
}
return false
}