get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/test/ports_test.go (about)

     1  // Copyright 2018-2019 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package test
    15  
    16  import (
    17  	"encoding/json"
    18  	"errors"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"get.pme.sh/pnats/server"
    27  )
    28  
    29  // waits until a calculated list of listeners is resolved or a timeout
    30  func waitForFile(path string, dur time.Duration) ([]byte, error) {
    31  	end := time.Now().Add(dur)
    32  	for time.Now().Before(end) {
    33  		if _, err := os.Stat(path); os.IsNotExist(err) {
    34  			time.Sleep(25 * time.Millisecond)
    35  			continue
    36  		} else {
    37  			return os.ReadFile(path)
    38  		}
    39  	}
    40  	return nil, errors.New("Timeout")
    41  }
    42  
    43  func portFile(dirname string) string {
    44  	return filepath.Join(dirname, fmt.Sprintf("%s_%d.ports", filepath.Base(os.Args[0]), os.Getpid()))
    45  }
    46  
    47  func TestPortsFile(t *testing.T) {
    48  	portFileDir := t.TempDir()
    49  
    50  	opts := DefaultTestOptions
    51  	opts.PortsFileDir = portFileDir
    52  	opts.Port = -1
    53  	opts.HTTPPort = -1
    54  	opts.ProfPort = -1
    55  	opts.Cluster.Port = -1
    56  	opts.Websocket.Port = -1
    57  	tc := &server.TLSConfigOpts{
    58  		CertFile: "./configs/certs/server-cert.pem",
    59  		KeyFile:  "./configs/certs/server-key.pem",
    60  	}
    61  	opts.Websocket.TLSConfig, _ = server.GenTLSConfig(tc)
    62  
    63  	s := RunServer(&opts)
    64  	// this for test cleanup in case we fail - will be ignored if server already shutdown
    65  	defer s.Shutdown()
    66  
    67  	ports := s.PortsInfo(5 * time.Second)
    68  
    69  	if ports == nil {
    70  		t.Fatal("services failed to start in 5 seconds")
    71  	}
    72  
    73  	// the pid file should be
    74  	portsFile := portFile(portFileDir)
    75  
    76  	if portsFile == "" {
    77  		t.Fatal("Expected a ports file")
    78  	}
    79  
    80  	// try to read a file here - the file should be a json
    81  	buf, err := waitForFile(portsFile, 5*time.Second)
    82  	if err != nil {
    83  		t.Fatalf("Could not read ports file: %v", err)
    84  	}
    85  	if len(buf) <= 0 {
    86  		t.Fatal("Expected a non-zero length ports file")
    87  	}
    88  
    89  	readPorts := server.Ports{}
    90  	json.Unmarshal(buf, &readPorts)
    91  
    92  	if len(readPorts.Nats) == 0 || !strings.HasPrefix(readPorts.Nats[0], "nats://") {
    93  		t.Fatal("Expected at least one nats url")
    94  	}
    95  
    96  	if len(readPorts.Monitoring) == 0 || !strings.HasPrefix(readPorts.Monitoring[0], "http://") {
    97  		t.Fatal("Expected at least one monitoring url")
    98  	}
    99  
   100  	if len(readPorts.Cluster) == 0 || !strings.HasPrefix(readPorts.Cluster[0], "nats://") {
   101  		t.Fatal("Expected at least one cluster listen url")
   102  	}
   103  
   104  	if len(readPorts.Profile) == 0 || !strings.HasPrefix(readPorts.Profile[0], "http://") {
   105  		t.Fatal("Expected at least one profile listen url")
   106  	}
   107  
   108  	if len(readPorts.WebSocket) == 0 || !strings.HasPrefix(readPorts.WebSocket[0], "wss://") {
   109  		t.Fatal("Expected at least one ws listen url")
   110  	}
   111  
   112  	// testing cleanup
   113  	s.Shutdown()
   114  	// if we called shutdown, the cleanup code should have kicked
   115  	if _, err := os.Stat(portsFile); os.IsNotExist(err) {
   116  		// good
   117  	} else {
   118  		t.Fatalf("the port file %s was not deleted", portsFile)
   119  	}
   120  }
   121  
   122  // makes a temp directory with two directories 'A' and 'B'
   123  // the location of the ports file is changed from dir A to dir B.
   124  func TestPortsFileReload(t *testing.T) {
   125  	tempDir := t.TempDir()
   126  
   127  	// make child temp dir A
   128  	dirA := filepath.Join(tempDir, "A")
   129  	os.MkdirAll(dirA, 0777)
   130  
   131  	// write the config file with a reference to A
   132  	config := fmt.Sprintf(`
   133  		ports_file_dir: "%s"
   134  		port: -1
   135  	`, dirA)
   136  	config = strings.Replace(config, "\\", "/", -1)
   137  	confPath := filepath.Join(tempDir, fmt.Sprintf("%d.conf", os.Getpid()))
   138  	if err := os.WriteFile(confPath, []byte(config), 0666); err != nil {
   139  		t.Fatalf("Error writing ports file (%s): %v", confPath, err)
   140  	}
   141  
   142  	opts, err := server.ProcessConfigFile(confPath)
   143  	if err != nil {
   144  		t.Fatalf("Error processing the configuration: %v", err)
   145  	}
   146  
   147  	s := RunServer(opts)
   148  	defer s.Shutdown()
   149  
   150  	ports := s.PortsInfo(5 * time.Second)
   151  	if ports == nil {
   152  		t.Fatal("services failed to start in 5 seconds")
   153  	}
   154  
   155  	// get the ports file path name
   156  	portsFileInA := portFile(dirA)
   157  	// the file should be in dirA
   158  	if !strings.HasPrefix(portsFileInA, dirA) {
   159  		t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirA, portsFileInA)
   160  	}
   161  	// wait for it
   162  	buf, err := waitForFile(portsFileInA, 5*time.Second)
   163  	if err != nil {
   164  		t.Fatalf("Could not read ports file: %v", err)
   165  	}
   166  	if len(buf) <= 0 {
   167  		t.Fatal("Expected a non-zero length ports file")
   168  	}
   169  
   170  	// change the configuration for the ports file to dirB
   171  	dirB := filepath.Join(tempDir, "B")
   172  	os.MkdirAll(dirB, 0777)
   173  
   174  	config = fmt.Sprintf(`
   175  		ports_file_dir: "%s"
   176  		port: -1
   177  	`, dirB)
   178  	config = strings.Replace(config, "\\", "/", -1)
   179  	if err := os.WriteFile(confPath, []byte(config), 0666); err != nil {
   180  		t.Fatalf("Error writing ports file (%s): %v", confPath, err)
   181  	}
   182  
   183  	// reload the server
   184  	if err := s.Reload(); err != nil {
   185  		t.Fatalf("error reloading server: %v", err)
   186  	}
   187  
   188  	// wait for the new file to show up
   189  	portsFileInB := portFile(dirB)
   190  	buf, err = waitForFile(portsFileInB, 5*time.Second)
   191  	if !strings.HasPrefix(portsFileInB, dirB) {
   192  		t.Fatalf("expected ports file to be in [%s] but was in [%s]", dirB, portsFileInB)
   193  	}
   194  	if err != nil {
   195  		t.Fatalf("Could not read ports file: %v", err)
   196  	}
   197  	if len(buf) <= 0 {
   198  		t.Fatal("Expected a non-zero length ports file")
   199  	}
   200  
   201  	// the file in dirA should have deleted
   202  	if _, err := os.Stat(portsFileInA); os.IsNotExist(err) {
   203  		// good
   204  	} else {
   205  		t.Fatalf("the port file %s was not deleted", portsFileInA)
   206  	}
   207  }