Generated valid label value (Kubernetes) - go

Label values in Kubernetes need to be valid.
See IsValidLabelValue()
For example the input I receive from a rest-API of a provider, which I want to write to a label: Dedicated Server 1U.
Is there a way to generate a valid label via Go from an arbitrary string?

you can have a function to do this, for example:
func generateLabel(input string) string {
input = strings.Replace(input, " ", "-", -1)
return "api-label=" + input
}
the function replaces the spaces in the received string to "-"
you can change the key to any string you like.
you can also add a regex check to make sure that the generated value complies with the label constraints. (this depends if any special characters are being received from the API)
To accept the string even when there are unwanted characters, check the below:
package main
import (
"regexp"
"strings"
"fmt"
)
func generateLabel(input string) string {
input = strings.Replace(input, " ", "-", -1)
re := regexp.MustCompile("[^a-zA-Z0-9-]")
input = re.ReplaceAllString(input, "")
re = regexp.MustCompile("^[^a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$")
input = re.ReplaceAllString(input, "")
return "api-label=" + input
}
func main() {
label := generateLabel("Dedicated Server 1U")
fmt.Println(label) // Output: "api-label=Dedicated-Server-1U"
label1 := generateLabel("Dedicated&test")
fmt.Println(label1) // Output: "api-label=Dedicatedtest"
label2 := generateLabel("Dedicated,test##&(*!great")
fmt.Println(label2) // Output: "api-label=Dedicatedtestgreat"
}

Related

A simple CLI that receives input from users

When I type in the command, give a space before hitting the enter button, it works fine, but it doesn't work if there is no space
I have tried several ways to fix this, but have been unable to
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
var notes []string
for {
fmt.Print("Enter a command and data: ")
reader := bufio.NewReader(os.Stdin)
line, _ := reader.ReadString('\n')
var joinedNote string
var note []string
splittedString := strings.Split(line, " ")
if splittedString[0] == "create" && len(splittedString) > 1 {
i := 1
for ; i < len(splittedString); i++ {
note = append(note, splittedString[i])
}
joinedNote = strings.Join(note, "")
notes = append(notes, joinedNote)
fmt.Println("[OK] The note was successfully created")
}
if splittedString[0] == "list" || string(line) == "list" {
for i, noteList := range notes {
newNote := strings.TrimSpace(noteList)
fmt.Printf("[Info] %d: %s!\n", i, newNote)
}
}
if splittedString[0] == "clear" || line == "clear" {
notes = nil
fmt.Println("[OK] All notes were successfully deleted")
}
if splittedString[0] == "exit" || line == "exit" {
fmt.Println("[Info] Bye!")
os.Exit(0)
}
}
}
The reason for this is that you are including the \n in line you capture from the user and without the space after it, the \n gets tagged onto the word you are looking for ( create\n does not equal create ). Easiest way to fix this is to manually remove the trailing \n with line = line[:len(line)-1].
Here is a little more a deep dive. First the ReadString method says it included the delimiter, in this case \n, you give it:
ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. So we know line will always have the \n at the end of it unless you manually remove it.
Your code worked when the word was followed by a space because your strings.Split(line," ") turned the input create \n into {"create","\n"}.

How can I clean the text for search using RegEx

I can use the below code to search if the text str contains any or both of the keys, i.e.if it contains "MS" or "dynamics" or both of them
package main
import (
"fmt"
"regexp"
)
func main() {
keys := []string{"MS", "dynamics"}
keysReg := fmt.Sprintf("(%s %s)|%s|%s", keys[0], keys[1], keys[0], keys[1]) // => "(MS dynamics)|MS|dynamics"
fmt.Println(keysReg)
str := "What is MS dynamics, is it a product from MS?"
re := regexp.MustCompile(`(?i)` + keysReg)
matches := re.FindAllString(str, -1)
fmt.Println("We found", len(matches), "matches, that are:", matches)
}
I want the user to enter his phrase, so I trim unwanted words and characters, then doing the search as per above.
Let's say the user input was: This,is,a,delimited,string and I need to build the keys variable dynamically to be (delimited string)|delimited|string so that I can search for my variable str for all the matches, so I wrote the below:
s := "This,is,a,delimited,string"
t := regexp.MustCompile(`(?i),|\.|this|is|a`) // backticks are used here to contain the expression, (?i) for case insensetive
v := t.Split(s, -1)
fmt.Println(len(v))
fmt.Println(v)
But I got the output as:
8
[ delimited string]
What is the wrong part in my cleaning of the input text, I'm expecting the output to be:
2
[delimited string]
Here is my playground
To quote the famous quip from Jamie Zawinski,
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Two things:
Instead of trying to weed out garbage from the string ("cleaning" it), extract complete words from it instead.
Unicode is a compilcated matter; so even after you have succeeded with extracting words, you have to make sure your words are properly "escaped" to not contain any characters which might be interpreted as RE syntax before building a regexp of them.
package main
import (
"errors"
"fmt"
"regexp"
"strings"
)
func build(words ...string) (*regexp.Regexp, error) {
var sb strings.Builder
switch len(words) {
case 0:
return nil, errors.New("empty input")
case 1:
return regexp.Compile(regexp.QuoteMeta(words[0]))
}
quoted := make([]string, len(words))
for i, w := range words {
quoted[i] = regexp.QuoteMeta(w)
}
sb.WriteByte('(')
for i, w := range quoted {
if i > 0 {
sb.WriteByte('\x20')
}
sb.WriteString(w)
}
sb.WriteString(`)|`)
for i, w := range quoted {
if i > 0 {
sb.WriteByte('|')
}
sb.WriteString(w)
}
return regexp.Compile(sb.String())
}
var words = regexp.MustCompile(`\pL+`)
func main() {
allWords := words.FindAllString("\tThis\v\x20\x20,\t\tis\t\t,?a!,¿delimited?,string‽", -1)
re, err := build(allWords...)
if err != nil {
panic(err)
}
fmt.Println(re)
}
Further reading:
https://pkg.go.dev/regexp/syntax
https://pkg.go.dev/regexp#QuoteMeta
https://pkg.go.dev/unicode#pkg-variables and https://pkg.go.dev/unicode#Categories

How to parse email addresses from a long string in Golang

How can I extract only email addresses from a long string in Golang? For example:
"a bunch of irrelevant text fjewiwofjfjvnvkdlslsosiejwoqlwpwpwo
mail=jim.halpert#gmail.com,ou=f,c=US
mail=apple.pie#gmail.com,ou=f,c=US
mail=hello.world#gmail.com,ou=f,c=US
mail=alex.alex#gmail.com,ou=f,c=US
mail=bob.jim#gmail.com,ou=people,ou=f,c=US
mail=arnold.schwarzenegger#gmail.com,ou=f,c=US"
This would return a list of all the emails:
[jim.halpert#gmail.com, apple.pie#gmail.com, etc...]
Each email address would begin with "mail=" and end with a comma ",".
For this you need to breakdown the long go string into parts that you need. You can do filtration and searching using Regular Expressions to match the email pattern you see above.
Here's a piece of code using Regular Expressions to first obtain the section with "mail=" then further format the email removing the trailing ,
import (
"fmt"
"regexp"
"strings"
)
func main() {
var re = regexp.MustCompile(`(?m)mail=[A-Za-z.#0-9]+\,`)
var str = `a bunch of irrelevant text fjewiwofjfjvnvkdlslsosiejwoqlwpwpwo
mail=jim.halpert#gmail.com,ou=f,c=US
mail=apple.pie#gmail.com,ou=f,c=US
mail=hello.world#gmail.com,ou=f,c=US
mail=alex.alex#gmail.com,ou=f,c=US
mail=bob.jim#gmail.com,ou=people,ou=f,c=US
mail=arnold.schwarzenegger#gmail.com,ou=f,c=US`
for i, match := range re.FindAllString(str, -1) {
fmt.Println(match, "found at index", i)
email := strings.Split(match, "=")[1]
email = strings.ReplaceAll(email, ",", "")
fmt.Print(email)
}
}
while i agree with the comment from user datenwolf here is another version which does not involve regular expressions.
It also handle more complex emails format including comma within the local parts. Something uneasy to implement using regexp.
see https://stackoverflow.com/a/2049510/11892070
import (
"bufio"
"fmt"
"strings"
)
var str = `a bunch of irrelevant text fjewiwofjfjvnvkdlslsosiejwoqlwpwpwo
mail=jim.halpert#gmail.com,ou=f,c=US
mail=apple.pie#gmail.com,ou=f,c=US
mail=hello.world#gmail.com,ou=f,c=US
mail=alex.alex#gmail.com,ou=f,c=US
mail=bob.jim#gmail.com,ou=people,ou=f,c=US
mail=arnold.schwarzenegger#gmail.com,ou=f,c=US
mail=(comented)arnold.schwarzenegger#gmail.com,ou=f,c=US
mail="(with comma inside)arnold,schwarzenegger#gmail.com",ou=f,c=US
mail=nocommaatall#gmail.com`
func main() {
var emails []string
sc := bufio.NewScanner(strings.NewReader(str))
for sc.Scan() {
t := sc.Text()
if !strings.HasPrefix(t, "mail=") {
continue
}
t = t[5:]
// Lookup for the next comma after the #.
at := strings.Index(t, "#")
comma := strings.Index(t[at:], ",")
if comma < 0 {
email := strings.TrimSpace(t)
emails = append(emails, email)
continue
}
comma += at
email := strings.TrimSpace(t[:comma])
emails = append(emails, email)
}
for _, e := range emails {
fmt.Println(e)
}
}
You can use this package to do that :
https://github.com/hamidteimouri/htutils/blob/main/htregex/htregex.go
// Emails finds all email strings
func Emails(text string) []string {
return match(text, EmailsRegex)
}
you can use an original package from golang is regexp.Compile or regexp.MustCompile
r, _ := regexp.Compile(regexEmail)
newVariable := `a bunch of irrelevant text fjewiwofjfjvnvkdlslsosiejwoqlwpwpwo
mail=jim.halpert#gmail.com,ou=f,c=US
mail=apple.pie#gmail.com,ou=f,c=US
mail=hello.world#gmail.com,ou=f,c=US
mail=alex.alex#gmail.com,ou=f,c=US
mail=bob.jim#gmail.com,ou=people,ou=f,c=US
mail=arnold.schwarzenegger#gmail.com,ou=f,c=US`
fmt.Printf("%#v\n", r.FindStringSubmatch(newVariable))
fmt.Printf("%#v\n", r.SubexpNames())

Go strings.Replace(input, "\n", "", -1) did not recognized

I'm trying to process the strings inputted by users, and wrote following code.
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
var input string
fileScanner := bufio.NewScanner(os.Stdin)
fileScanner.Scan()
input = fileScanner.Text()
replaced := strings.Replace(input, "\n", "", -1)
fmt.Println(replace)
}
But I found "\n" was not replaced to "". "\n" does not seems as a string.
I tried it "." or "," instead of "\n" and it works.
I just started learning Go and this question might be too fundamental, but I appreciate for any advice.
"\" characters are processed as escape characters within a formated string and hence wont be replaced by strings.Replace
If you are really trying to replace the \n character itself, not the new line putting it inside a raw string literal should help. Refer the code below:
package main
import (
"fmt"
"strings"
)
func main() {
var input string
input = `\naaa`
replaced := strings.Replace(input, `\n`, "", -1)
fmt.Println(replaced)
}

How to build a URL / Query in Golang

Background -
I need to build a URL / query based on user input from a form that will be used to make an API call.
Problem -
When building the URL, the params are not properly escaped. For example, the query "bad santa" ends up with a space between it instead of "+".
Current Output -
e.g. https://api.example.org/3/search/movie?query=bad
santa&api_key=#######
Expected Output -
e.g. https://api.example.org/3/search/movie?query=bad+santa&api_key=#######
Code Example -
Root URL -
var SearchUrl = "https://www.example.org/3/search/movie?query="
Get params taken from user input -
var MovieSearch []string = r.Form["GetSearchKey"]
API Key -
var apiKey = "&api_key=######"
I am using the ArrayToString() to parse the form input data
func ArrayToString(array []string) string{
str := strings.Join(array, "+")
return str
}
Then building the URL -
var SearchUrl = "https://api.example.org/3/search/movie?query="
var MovieSearch []string = r.Form["GetSearchKey"]
var apiKey = "&api_key=########"
UrlBuild := []string {SearchUrl, ArrayToString(MovieSearch), apiKey}
OUTPUT_STRING := ArrayToString(UrlBuild)
Question -
How to build a URL with user input GET params that are escaped properly?
Normally, one should use url package's Values.
Here's an example, that does what I think you want, on play
Both a simple main, and in http.HandlerFunc form:
package main
import "fmt"
import "net/url"
import "net/http"
func main() {
baseURL := "https://www.example.org/3/search/movie"
v := url.Values{}
v.Set("query", "this is a value")
perform := baseURL + "?" + v.Encode()
fmt.Println("Perform:", perform)
}
func formHandler(w http.ResponseWriter, r *http.Request) {
baseURL := "https://www.example.org/3/search/movie"
v := url.Values{}
v.Set("query", r.Form.Get("GetSearchKey")) // take GetSearchKey from submitted form
v.Set("api_ley", "YOURKEY") // whatever your api key is
perform := baseURL + "?" + v.Encode() // put it all together
fmt.Println("Perform:", perform) // do something with it
}
Output:
Perform: https://www.example.org/3/search/movie?query=this+is+a+value
Notice how the values are put in to query string, properly escaped, for you.
You can escape parameters using https://golang.org/pkg/net/url/#QueryEscape, instead of doing it yourself.
Besides you should be using https://golang.org/pkg/net/url/#URL to build up your url:
params := fmt.Sprintf("?query=%s&api_key=######", url.QueryEscape("name"))
perform := url.URL{
Scheme: "https",
Host: "api.example.com",
Path: "3/search/movie",
RawQuery: params,
}
fmt.Println(perform) // <- Calls .String()
I recommend to check https://golang.org/doc/effective_go.html.
If your data comes in []string:
func ArrayToQuery(values []string) string {
return url.QueryEscape(strings.Join(values, " "))
}
If MovieSearch contains one element with the value "bad santa", what you're seeing looks correct. It's joining those three strings and putting "+" between them.
If there is a space in the word you will need to replace it.
Example
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Replace("bad santa", " ", "+", -1))
}
So you should probably do it like this
func main() {
a := []string{"bad", "santa"}
fmt.Printf("%q\n", a)
j := ArrayToString(a)
strings.Replace(j, " ", "+",-1)
fmt.Printf("%q\n", j)
}
Here is a link to the Go Documentation - https://golang.org/pkg/strings/#Replace

Resources