Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions bigip.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,24 @@ func NewTokenSession(bigipConfig *Config) (b *BigIP, err error) {
Timeout struct {
Timeout int64
}
RefreshToken interface{} `json:"refreshToken,omitempty"`
}

type timeoutReq struct {
Timeout int64 `json:"timeout"`
}

// type timeoutResp struct {
// Timeout struct {
// Timeout int64
// }
// }

if bigipConfig.LoginReference == "" {
bigipConfig.LoginReference = "tmos"
}
auth := authReq{
bigipConfig.Username,
bigipConfig.Password,
bigipConfig.LoginReference,
}

marshalJSONauth, err := json.Marshal(auth)
if err != nil {
return
}

req := &APIRequest{
Method: "post",
URL: "mgmt/shared/authn/login",
Expand Down Expand Up @@ -227,7 +222,7 @@ func NewTokenSession(bigipConfig *Config) (b *BigIP, err error) {
b.Token = aresp.Token.Token

//Once we have obtained a token, we should actually apply the configured timeout to it
if time.Duration(aresp.Timeout.Timeout)*time.Second != bigipConfig.ConfigOptions.TokenTimeout { // The inital value is the max timespan
if aresp.RefreshToken == nil && time.Duration(aresp.Timeout.Timeout)*time.Second != bigipConfig.ConfigOptions.TokenTimeout { // The inital value is the max timespan
timeout := timeoutReq{
int64(bigipConfig.ConfigOptions.TokenTimeout.Seconds()),
}
Expand Down Expand Up @@ -617,6 +612,10 @@ func (b *BigIP) getForEntity(e interface{}, path ...string) (error, bool) {
}

resp, err := b.APICall(req)
// add log for debugging
// If we get an error, we need to check if it is a 404 error.
// check error code and response body

if err != nil {
var reqError RequestError
json.Unmarshal(resp, &reqError)
Expand Down
34 changes: 34 additions & 0 deletions bigiq_functional_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package bigip

import (
"os"
"testing"
)

func TestGetManagedDevicesFunc(t *testing.T) {
host := os.Getenv("BIGIQ_HOST")
user := os.Getenv("BIGIQ_USER")
pass := os.Getenv("BIGIQ_PASSWORD")
if host == "" || user == "" || pass == "" {
t.Skip("Environment variables BIGIQ_HOST, BIGIQ_USER, BIGIQ_PASSWORD must be set")
}
b, err := NewTokenSession(&Config{
Address: host,
Username: user,
Password: pass,
CertVerifyDisable: true, // Disable cert verification for testing
})
if err != nil {
t.Fatalf("NewTokenSession failed: %v", err)
}
devices, err := b.GetManagedDevices()
if err != nil {
t.Fatalf("GetManagedDevices failed: %v", err)
}
if devices == nil || len(devices.DevicesInfo) == 0 {
t.Errorf("No managed devices found")
}
for _, d := range devices.DevicesInfo {
t.Logf("Managed Device: Address=%s, Hostname=%s, UUID=%s", d.Address, d.Hostname, d.UUID)
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/f5devcentral/go-bigip

go 1.20
go 1.22

require github.com/stretchr/testify v1.2.1

Expand Down
27 changes: 14 additions & 13 deletions ltm.go
Original file line number Diff line number Diff line change
Expand Up @@ -1623,8 +1623,8 @@ type Originsrecords struct {
}

type Originsrecord struct {
Name string `json:"name"`
app_service string `json:"appService,omitempty"`
Name string `json:"name"`
AppService string `json:"appService,omitempty"`
}

func (p *Snat) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -4259,17 +4259,7 @@ func (b *BigIP) AddRequestLogProfile(config *RequestLogProfile) error {
return b.post(config, uriLtm, uriProfile, uriRequestLog)
}

// DeleteRequestLogProfile removes a Request Log profile.
func (b *BigIP) DeleteRequestLogProfile(name string) error {
return b.delete(uriLtm, uriProfile, uriRequestLog, name)
}

// ModifyRequestLogProfile allows you to change any attribute of a RequestLog profile.
// Fields that can be modified are referenced in the RequestLogProfile struct.
func (b *BigIP) ModifyRequestLogProfile(name string, config *RequestLogProfile) error {
return b.patch(config, uriLtm, uriProfile, uriRequestLog, name)
}

// GetRequestLogProfile gets a request log profile by name. Returns nil if the request log profile does not exist
func (b *BigIP) GetRequestLogProfile(name string) (*RequestLogProfile, error) {
var requestLogProfile RequestLogProfile
err, ok := b.getForEntity(&requestLogProfile, uriLtm, uriProfile, uriRequestLog, name)
Expand All @@ -4284,6 +4274,17 @@ func (b *BigIP) GetRequestLogProfile(name string) (*RequestLogProfile, error) {
return &requestLogProfile, nil
}

// DeleteRequestLogProfile removes a Request Log profile.
func (b *BigIP) DeleteRequestLogProfile(name string) error {
return b.delete(uriLtm, uriProfile, uriRequestLog, name)
}

// ModifyRequestLogProfile allows you to change any attribute of a RequestLog profile.
// Fields that can be modified are referenced in the RequestLogProfile struct.
func (b *BigIP) ModifyRequestLogProfile(name string, config *RequestLogProfile) error {
return b.patch(config, uriLtm, uriProfile, uriRequestLog, name)
}

type BotDefenseProfile struct {
Name string `json:"name,omitempty"`
Partition string `json:"partition,omitempty"`
Expand Down
76 changes: 76 additions & 0 deletions ltm_functional_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package bigip

import (
"os"
"testing"
)

// Functional test for IRules() using a real or integration environment
func TestFunctionalIRules(t *testing.T) {
address := os.Getenv("BIGIP_HOST")
username := os.Getenv("BIGIP_USER")
password := os.Getenv("BIGIP_PASSWORD")
t.Logf("BIGIP_HOST=%s, BIGIP_USER=%s", address, username) // Debug log for env vars
if address == "" || username == "" || password == "" {
t.Skip("BIGIP environment variables not set")
}
cfg := &Config{
Address: address,
Username: username,
Password: password,
CertVerifyDisable: true, // Disable certificate validation for testing
}
t.Logf("Config: %+v", cfg) // Debug log for config
client, err := NewTokenSession(cfg)
if err != nil {
t.Fatalf("NewTokenSession error: %v", err)
}

rules, err := client.IRules()
if err != nil {
t.Fatalf("IRules() error: %v", err)
}
if rules == nil || len(rules.IRules) == 0 {
t.Errorf("No iRules returned, got: %+v", rules)
} else {
for _, rule := range rules.IRules {
t.Logf("iRule: %+v", rule)
}
}
}

// Functional test to get a specific iRule by name
// Functional test to test the IRule() method for fetching a specific iRule by name
func TestGetSpecificIRule(t *testing.T) {
address := os.Getenv("BIGIP_HOST")
username := os.Getenv("BIGIP_USER")
password := os.Getenv("BIGIP_PASSWORD")

iruleName := "default_f5_healt"
t.Logf("BIGIP_HOST=%s, BIGIP_USER=%s, BIGIP_IRULE_NAME=%s", address, username, iruleName)
if address == "" || username == "" || password == "" || iruleName == "" {
t.Skip("BIGIP environment variables not set or iRule name missing")
}
cfg := &Config{
Address: address,
Username: username,
Password: password,
CertVerifyDisable: true,
}
client, err := NewTokenSession(cfg)
if err != nil {
t.Fatalf("NewTokenSession error: %v", err)
}

irule, err := client.IRule(iruleName)
if err != nil {
t.Fatalf("IRule error: %v", err)
}
if irule == nil {
t.Errorf("iRule '%s' not found", iruleName)
} else {
t.Logf("Found iRule '%s': %+v", iruleName, irule)
}
}

// Add more functional tests for other LTM features as needed
Empty file added ltm_irules_test.go
Empty file.
16 changes: 10 additions & 6 deletions net_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package bigip

import (
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"io/ioutil"
)

type NetTestSuite struct {
Expand All @@ -21,15 +21,15 @@ type NetTestSuite struct {

func (s *NetTestSuite) SetupSuite() {
s.Server = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
body, _ := io.ReadAll(r.Body)
s.LastRequestBody = string(body)
s.LastRequest = r
if s.ResponseFunc != nil {
s.ResponseFunc(w, r)
}
}))

s.Client = NewSession(s.Server.URL, "", "", "", nil)
s.Client = NewSession(&Config{Address: s.Server.URL})
}

func (s *NetTestSuite) TearDownSuite() {
Expand Down Expand Up @@ -131,7 +131,8 @@ func (s *NetTestSuite) TestSelfIPs() {
}

func (s *NetTestSuite) TestCreateSelfIP() {
err := s.Client.CreateSelfIP("0.0.0.0", "0.0.0.0/20", "vlan")
ip := &SelfIP{Name: "0.0.0.0", Address: "0.0.0.0/20", Vlan: "vlan"}
err := s.Client.CreateSelfIP(ip)

assert.Nil(s.T(), err)
assertRestCall(s, "POST", "/mgmt/tm/net/self", `{"name":"0.0.0.0","address":"0.0.0.0/20", "vlan":"vlan"}`)
Expand Down Expand Up @@ -243,7 +244,9 @@ func (s *NetTestSuite) TestVlans() {
}

func (s *NetTestSuite) TestCreateVLan() {
err := s.Client.CreateVlan("name", 1)
vlan := &Vlan{Name: "name", Tag: 1}
// SFlow field uses the zero value (empty struct)
err := s.Client.CreateVlan(vlan)

assert.Nil(s.T(), err)
assertRestCall(s, "POST", "/mgmt/tm/net/vlan", `{"name":"name", "tag":1, "sflow":{}}`)
Expand Down Expand Up @@ -295,7 +298,8 @@ func (s *NetTestSuite) TestRoutes() {
}

func (s *NetTestSuite) TestCreateRoute() {
err := s.Client.CreateRoute("default_route", "default", "0.0.0.0")
route := &Route{Name: "default_route", Network: "default", Gateway: "0.0.0.0"}
err := s.Client.CreateRoute(route)

assert.Nil(s.T(), err)
assertRestCall(s, "POST", "/mgmt/tm/net/route", `{"name":"default_route", "network":"default", "gw":"0.0.0.0"}`)
Expand Down