mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge branch 'main' into show-nodes-online
This commit is contained in:
		
						commit
						bfa9ed814d
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -31,3 +31,5 @@ test_output/
 | 
				
			|||||||
# Nix build output
 | 
					# Nix build output
 | 
				
			||||||
result
 | 
					result
 | 
				
			||||||
.direnv/
 | 
					.direnv/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					integration_test/etc/config.dump.yaml
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@
 | 
				
			|||||||
- Use new ACL syntax [#618](https://github.com/juanfont/headscale/pull/618)
 | 
					- Use new ACL syntax [#618](https://github.com/juanfont/headscale/pull/618)
 | 
				
			||||||
- Add -c option to specify config file from command line [#285](https://github.com/juanfont/headscale/issues/285) [#612](https://github.com/juanfont/headscale/pull/601)
 | 
					- Add -c option to specify config file from command line [#285](https://github.com/juanfont/headscale/issues/285) [#612](https://github.com/juanfont/headscale/pull/601)
 | 
				
			||||||
- Add configuration option to allow Tailscale clients to use a random WireGuard port. [kb/1181/firewalls](https://tailscale.com/kb/1181/firewalls) [#624](https://github.com/juanfont/headscale/pull/624)
 | 
					- Add configuration option to allow Tailscale clients to use a random WireGuard port. [kb/1181/firewalls](https://tailscale.com/kb/1181/firewalls) [#624](https://github.com/juanfont/headscale/pull/624)
 | 
				
			||||||
 | 
					- Improve obtuse UX regarding missing configuration (`ephemeral_node_inactivity_timeout` not set) [#639](https://github.com/juanfont/headscale/pull/639)
 | 
				
			||||||
- Fix nodes being shown as 'offline' in `tailscale status` [648](https://github.com/juanfont/headscale/pull/648)
 | 
					- Fix nodes being shown as 'offline' in `tailscale status` [648](https://github.com/juanfont/headscale/pull/648)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 0.15.0 (2022-03-20)
 | 
					## 0.15.0 (2022-03-20)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
				
			|||||||
# Calculate version
 | 
					# Calculate version
 | 
				
			||||||
version = $(git describe --always --tags --dirty)
 | 
					version ?= $(shell git describe --always --tags --dirty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
 | 
					rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										32
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								README.md
									
									
									
									
									
								
							@ -195,6 +195,15 @@ make build
 | 
				
			|||||||
            <sub style="font-size:14px"><b>Nico</b></sub>
 | 
					            <sub style="font-size:14px"><b>Nico</b></sub>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
    </td>
 | 
					    </td>
 | 
				
			||||||
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
 | 
					        <a href=https://github.com/huskyii>
 | 
				
			||||||
 | 
					            <img src=https://avatars.githubusercontent.com/u/5499746?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jiang Zhu/>
 | 
				
			||||||
 | 
					            <br />
 | 
				
			||||||
 | 
					            <sub style="font-size:14px"><b>Jiang Zhu</b></sub>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    </td>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					<tr>
 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
        <a href=https://github.com/e-zk>
 | 
					        <a href=https://github.com/e-zk>
 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/58356365?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=e-zk/>
 | 
					            <img src=https://avatars.githubusercontent.com/u/58356365?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=e-zk/>
 | 
				
			||||||
@ -202,8 +211,6 @@ make build
 | 
				
			|||||||
            <sub style="font-size:14px"><b>e-zk</b></sub>
 | 
					            <sub style="font-size:14px"><b>e-zk</b></sub>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
    </td>
 | 
					    </td>
 | 
				
			||||||
</tr>
 | 
					 | 
				
			||||||
<tr>
 | 
					 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
        <a href=https://github.com/arch4ngel>
 | 
					        <a href=https://github.com/arch4ngel>
 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/11574161?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Justin Angel/>
 | 
					            <img src=https://avatars.githubusercontent.com/u/11574161?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Justin Angel/>
 | 
				
			||||||
@ -218,13 +225,6 @@ make build
 | 
				
			|||||||
            <sub style="font-size:14px"><b>Alessandro (Ale) Segala</b></sub>
 | 
					            <sub style="font-size:14px"><b>Alessandro (Ale) Segala</b></sub>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
    </td>
 | 
					    </td>
 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					 | 
				
			||||||
        <a href=https://github.com/huskyii>
 | 
					 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/5499746?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jiang Zhu/>
 | 
					 | 
				
			||||||
            <br />
 | 
					 | 
				
			||||||
            <sub style="font-size:14px"><b>Jiang Zhu</b></sub>
 | 
					 | 
				
			||||||
        </a>
 | 
					 | 
				
			||||||
    </td>
 | 
					 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
        <a href=https://github.com/unreality>
 | 
					        <a href=https://github.com/unreality>
 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/352522?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=unreality/>
 | 
					            <img src=https://avatars.githubusercontent.com/u/352522?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=unreality/>
 | 
				
			||||||
@ -292,6 +292,13 @@ make build
 | 
				
			|||||||
    </td>
 | 
					    </td>
 | 
				
			||||||
</tr>
 | 
					</tr>
 | 
				
			||||||
<tr>
 | 
					<tr>
 | 
				
			||||||
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
 | 
					        <a href=https://github.com/iSchluff>
 | 
				
			||||||
 | 
					            <img src=https://avatars.githubusercontent.com/u/1429641?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Anton Schubert/>
 | 
				
			||||||
 | 
					            <br />
 | 
				
			||||||
 | 
					            <sub style="font-size:14px"><b>Anton Schubert</b></sub>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    </td>
 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
        <a href=https://github.com/deonthomasgy>
 | 
					        <a href=https://github.com/deonthomasgy>
 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/150036?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Deon Thomas/>
 | 
					            <img src=https://avatars.githubusercontent.com/u/150036?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Deon Thomas/>
 | 
				
			||||||
@ -327,13 +334,6 @@ make build
 | 
				
			|||||||
            <sub style="font-size:14px"><b>Stefan Majer</b></sub>
 | 
					            <sub style="font-size:14px"><b>Stefan Majer</b></sub>
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
    </td>
 | 
					    </td>
 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					 | 
				
			||||||
        <a href=https://github.com/iSchluff>
 | 
					 | 
				
			||||||
            <img src=https://avatars.githubusercontent.com/u/1429641?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Anton Schubert/>
 | 
					 | 
				
			||||||
            <br />
 | 
					 | 
				
			||||||
            <sub style="font-size:14px"><b>Anton Schubert</b></sub>
 | 
					 | 
				
			||||||
        </a>
 | 
					 | 
				
			||||||
    </td>
 | 
					 | 
				
			||||||
</tr>
 | 
					</tr>
 | 
				
			||||||
<tr>
 | 
					<tr>
 | 
				
			||||||
    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
					    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
 | 
				
			||||||
 | 
				
			|||||||
@ -7,12 +7,10 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/juanfont/headscale"
 | 
						"github.com/juanfont/headscale"
 | 
				
			||||||
	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
						v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
				
			||||||
	"github.com/rs/zerolog/log"
 | 
						"github.com/rs/zerolog/log"
 | 
				
			||||||
	"github.com/spf13/viper"
 | 
					 | 
				
			||||||
	"google.golang.org/grpc"
 | 
						"google.golang.org/grpc"
 | 
				
			||||||
	"google.golang.org/grpc/credentials"
 | 
						"google.golang.org/grpc/credentials"
 | 
				
			||||||
	"google.golang.org/grpc/credentials/insecure"
 | 
						"google.golang.org/grpc/credentials/insecure"
 | 
				
			||||||
@ -29,21 +27,6 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
 | 
				
			|||||||
		return nil, fmt.Errorf("failed to load configuration while creating headscale instance: %w", err)
 | 
							return nil, fmt.Errorf("failed to load configuration while creating headscale instance: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
 | 
					 | 
				
			||||||
	// to avoid races
 | 
					 | 
				
			||||||
	minInactivityTimeout, _ := time.ParseDuration("65s")
 | 
					 | 
				
			||||||
	if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
 | 
					 | 
				
			||||||
		// TODO: Find a better way to return this text
 | 
					 | 
				
			||||||
		//nolint
 | 
					 | 
				
			||||||
		err := fmt.Errorf(
 | 
					 | 
				
			||||||
			"ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s",
 | 
					 | 
				
			||||||
			viper.GetString("ephemeral_node_inactivity_timeout"),
 | 
					 | 
				
			||||||
			minInactivityTimeout,
 | 
					 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	app, err := headscale.NewHeadscale(cfg)
 | 
						app, err := headscale.NewHeadscale(cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								config.go
									
									
									
									
									
								
							@ -160,7 +160,11 @@ func LoadConfig(path string, isFile bool) error {
 | 
				
			|||||||
	viper.SetDefault("logtail.enabled", false)
 | 
						viper.SetDefault("logtail.enabled", false)
 | 
				
			||||||
	viper.SetDefault("randomize_client_port", false)
 | 
						viper.SetDefault("randomize_client_port", false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						viper.SetDefault("ephemeral_node_inactivity_timeout", "120s")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := viper.ReadInConfig(); err != nil {
 | 
						if err := viper.ReadInConfig(); err != nil {
 | 
				
			||||||
 | 
							log.Warn().Err(err).Msg("Failed to read configuration from disk")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return fmt.Errorf("fatal error reading config file: %w", err)
 | 
							return fmt.Errorf("fatal error reading config file: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -202,6 +206,17 @@ func LoadConfig(path string, isFile bool) error {
 | 
				
			|||||||
			EnforcedClientAuth)
 | 
								EnforcedClientAuth)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
 | 
				
			||||||
 | 
						// to avoid races
 | 
				
			||||||
 | 
						minInactivityTimeout, _ := time.ParseDuration("65s")
 | 
				
			||||||
 | 
						if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
 | 
				
			||||||
 | 
							errorText += fmt.Sprintf(
 | 
				
			||||||
 | 
								"Fatal config error: ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s",
 | 
				
			||||||
 | 
								viper.GetString("ephemeral_node_inactivity_timeout"),
 | 
				
			||||||
 | 
								minInactivityTimeout,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if errorText != "" {
 | 
						if errorText != "" {
 | 
				
			||||||
		//nolint
 | 
							//nolint
 | 
				
			||||||
		return errors.New(strings.TrimSuffix(errorText, "\n"))
 | 
							return errors.New(strings.TrimSuffix(errorText, "\n"))
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								derp.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								derp.go
									
									
									
									
									
								
							@ -152,16 +152,7 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
 | 
				
			|||||||
				h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
 | 
									h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			namespaces, err := h.ListNamespaces()
 | 
								h.setLastStateChangeToNow()
 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Error().
 | 
					 | 
				
			||||||
					Err(err).
 | 
					 | 
				
			||||||
					Msg("Failed to fetch namespaces")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			for _, namespace := range namespaces {
 | 
					 | 
				
			||||||
				h.setLastStateChangeToNow(namespace.Name)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										14
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								utils.go
									
									
									
									
									
								
							@ -325,11 +325,17 @@ func GenerateRandomStringURLSafe(n int) (string, error) {
 | 
				
			|||||||
// number generator fails to function correctly, in which
 | 
					// number generator fails to function correctly, in which
 | 
				
			||||||
// case the caller should not continue.
 | 
					// case the caller should not continue.
 | 
				
			||||||
func GenerateRandomStringDNSSafe(n int) (string, error) {
 | 
					func GenerateRandomStringDNSSafe(n int) (string, error) {
 | 
				
			||||||
	str, err := GenerateRandomStringURLSafe(n)
 | 
						var str string
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						for len(str) < n {
 | 
				
			||||||
 | 
							str, err = GenerateRandomStringURLSafe(n)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""))
 | 
						return str[:n], nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return str[:n], err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func IsStringInSlice(slice []string, str string) bool {
 | 
					func IsStringInSlice(slice []string, str string) bool {
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
 | 
				
			|||||||
		MachineKey:     "foo",
 | 
							MachineKey:     "foo",
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        "bar",
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
							DiscoKey:       "faa",
 | 
				
			||||||
		Hostname:           "testmachine",
 | 
							Hostname:       "testmachine",
 | 
				
			||||||
		NamespaceID:    namespace.ID,
 | 
							NamespaceID:    namespace.ID,
 | 
				
			||||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
							RegisterMethod: RegisterMethodAuthKey,
 | 
				
			||||||
		AuthKeyID:      uint(pak.ID),
 | 
							AuthKeyID:      uint(pak.ID),
 | 
				
			||||||
@ -82,7 +82,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
 | 
				
			|||||||
			MachineKey:     "foo",
 | 
								MachineKey:     "foo",
 | 
				
			||||||
			NodeKey:        "bar",
 | 
								NodeKey:        "bar",
 | 
				
			||||||
			DiscoKey:       "faa",
 | 
								DiscoKey:       "faa",
 | 
				
			||||||
			Hostname:           "testmachine",
 | 
								Hostname:       "testmachine",
 | 
				
			||||||
			NamespaceID:    namespace.ID,
 | 
								NamespaceID:    namespace.ID,
 | 
				
			||||||
			RegisterMethod: RegisterMethodAuthKey,
 | 
								RegisterMethod: RegisterMethodAuthKey,
 | 
				
			||||||
			AuthKeyID:      uint(pak.ID),
 | 
								AuthKeyID:      uint(pak.ID),
 | 
				
			||||||
@ -172,7 +172,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
 | 
				
			|||||||
		MachineKey:     "foo",
 | 
							MachineKey:     "foo",
 | 
				
			||||||
		NodeKey:        "bar",
 | 
							NodeKey:        "bar",
 | 
				
			||||||
		DiscoKey:       "faa",
 | 
							DiscoKey:       "faa",
 | 
				
			||||||
		Hostname:           "testmachine",
 | 
							Hostname:       "testmachine",
 | 
				
			||||||
		NamespaceID:    namespace.ID,
 | 
							NamespaceID:    namespace.ID,
 | 
				
			||||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
							RegisterMethod: RegisterMethodAuthKey,
 | 
				
			||||||
		AuthKeyID:      uint(pak.ID),
 | 
							AuthKeyID:      uint(pak.ID),
 | 
				
			||||||
@ -185,3 +185,15 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
 | 
				
			|||||||
	c.Assert(len(ips2), check.Equals, 1)
 | 
						c.Assert(len(ips2), check.Equals, 1)
 | 
				
			||||||
	c.Assert(ips2[0].String(), check.Equals, expected.String())
 | 
						c.Assert(ips2[0].String(), check.Equals, expected.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *Suite) TestGenerateRandomStringDNSSafe(c *check.C) {
 | 
				
			||||||
 | 
						for i := 0; i < 100000; i++ {
 | 
				
			||||||
 | 
							str, err := GenerateRandomStringDNSSafe(8)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								c.Error(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(str) != 8 {
 | 
				
			||||||
 | 
								c.Error("invalid length", len(str), str)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user