@@ -14,6 +14,12 @@ const APIVersion = 2
1414
1515const streamNotConfiguredCode = "StreamNotConfigured"
1616
17+ // Stream is a stream context parameter.
18+ const Stream = true
19+
20+ // HTTP is a HTTP context parameter.
21+ const HTTP = false
22+
1723// NginxClient lets you access NGINX Plus API.
1824type NginxClient struct {
1925 apiEndpoint string
@@ -806,3 +812,243 @@ func (client *NginxClient) getStreamUpstreams() (*StreamUpstreams, error) {
806812 }
807813 return & upstreams , nil
808814}
815+
816+ // KeyValPairs are the key-value pairs stored in a zone.
817+ type KeyValPairs map [string ]string
818+
819+ // KeyValPairsByZone are the KeyValPairs for all zones, by zone name.
820+ type KeyValPairsByZone map [string ]KeyValPairs
821+
822+ // GetKeyValPairs fetches key/value pairs for a given HTTP zone.
823+ func (client * NginxClient ) GetKeyValPairs (zone string ) (KeyValPairs , error ) {
824+ return client .getKeyValPairs (zone , HTTP )
825+ }
826+
827+ // GetStreamKeyValPairs fetches key/value pairs for a given Stream zone.
828+ func (client * NginxClient ) GetStreamKeyValPairs (zone string ) (KeyValPairs , error ) {
829+ return client .getKeyValPairs (zone , Stream )
830+ }
831+
832+ func (client * NginxClient ) getKeyValPairs (zone string , stream bool ) (KeyValPairs , error ) {
833+ base := "http"
834+ if stream {
835+ base = "stream"
836+ }
837+ if zone == "" {
838+ return nil , fmt .Errorf ("zone required" )
839+ }
840+ var keyValPairs KeyValPairs
841+ err := client .get (fmt .Sprintf ("%v/keyvals/%v" , base , zone ), & keyValPairs )
842+ if err != nil {
843+ return nil , fmt .Errorf ("failed to get keyvals for zone: %v/%v: %v" , base , zone , err )
844+ }
845+ return keyValPairs , nil
846+ }
847+
848+ // GetAllKeyValPairs fetches all key/value pairs for all HTTP zones.
849+ func (client * NginxClient ) GetAllKeyValPairs () (KeyValPairsByZone , error ) {
850+ return client .getAllKeyValPairs (HTTP )
851+ }
852+
853+ // GetAllStreamKeyValPairs fetches all key/value pairs for all Stream zones.
854+ func (client * NginxClient ) GetAllStreamKeyValPairs () (KeyValPairsByZone , error ) {
855+ return client .getAllKeyValPairs (Stream )
856+ }
857+
858+ func (client * NginxClient ) getAllKeyValPairs (stream bool ) (KeyValPairsByZone , error ) {
859+ base := "http"
860+ if stream {
861+ base = "stream"
862+ }
863+ var keyValPairsByZone KeyValPairsByZone
864+ err := client .get (fmt .Sprintf ("%v/keyvals" , base ), & keyValPairsByZone )
865+ if err != nil {
866+ return nil , fmt .Errorf ("failed to get keyvals for all %v zones: %v" , base , err )
867+ }
868+ return keyValPairsByZone , nil
869+ }
870+
871+ // AddKeyValPair adds a new key/value pair to a given HTTP zone.
872+ func (client * NginxClient ) AddKeyValPair (zone string , key string , val string ) error {
873+ return client .addKeyValPair (zone , key , val , HTTP )
874+ }
875+
876+ // AddStreamKeyValPair adds a new key/value pair to a given Stream zone.
877+ func (client * NginxClient ) AddStreamKeyValPair (zone string , key string , val string ) error {
878+ return client .addKeyValPair (zone , key , val , Stream )
879+ }
880+
881+ func (client * NginxClient ) addKeyValPair (zone string , key string , val string , stream bool ) error {
882+ base := "http"
883+ if stream {
884+ base = "stream"
885+ }
886+ path := fmt .Sprintf ("%v/keyvals" , base )
887+ url := fmt .Sprintf ("%v/%v/%v" , client .apiEndpoint , APIVersion , path )
888+ if zone != "" {
889+ url = fmt .Sprintf ("%v/%v" , url , zone )
890+ } else {
891+ return fmt .Errorf ("zone required" )
892+ }
893+
894+ jsonInput , err := json .Marshal (KeyValPairs {key : val })
895+ if err != nil {
896+ return fmt .Errorf ("failed to marshall input: %v" , err )
897+ }
898+
899+ resp , err := client .httpClient .Post (url , "application/json" , bytes .NewBuffer (jsonInput ))
900+ if err != nil {
901+ return fmt .Errorf ("failed to create post request: %v" , err )
902+ }
903+ defer resp .Body .Close ()
904+
905+ if resp .StatusCode != http .StatusCreated {
906+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
907+ "expected %v response, got %v" ,
908+ http .StatusCreated , resp .StatusCode ))
909+ }
910+ return nil
911+ }
912+
913+ // ModifyKeyValPair modifies the value of an existing key in a given HTTP zone.
914+ func (client * NginxClient ) ModifyKeyValPair (zone string , key string , val string ) error {
915+ return client .modifyKeyValPair (zone , key , val , HTTP )
916+ }
917+
918+ // ModifyStreamKeyValPair modifies the value of an existing key in a given Stream zone.
919+ func (client * NginxClient ) ModifyStreamKeyValPair (zone string , key string , val string ) error {
920+ return client .modifyKeyValPair (zone , key , val , Stream )
921+ }
922+
923+ func (client * NginxClient ) modifyKeyValPair (zone string , key string , val string , stream bool ) error {
924+ base := "http"
925+ if stream {
926+ base = "stream"
927+ }
928+ path := fmt .Sprintf ("%v/keyvals" , base )
929+ url := fmt .Sprintf ("%v/%v/%v" , client .apiEndpoint , APIVersion , path )
930+ if zone != "" {
931+ url = fmt .Sprintf ("%v/%v" , url , zone )
932+ } else {
933+ return fmt .Errorf ("zone required" )
934+ }
935+
936+ jsonInput , err := json .Marshal (KeyValPairs {key : val })
937+ if err != nil {
938+ return fmt .Errorf ("failed to marshall input: %v" , err )
939+ }
940+ req , err := http .NewRequest (http .MethodPatch , url , bytes .NewBuffer (jsonInput ))
941+ if err != nil {
942+ return fmt .Errorf ("failed to create a patch request: %v" , err )
943+ }
944+ req .Header .Set ("Content-Type" , "application/json" )
945+
946+ resp , err := client .httpClient .Do (req )
947+ if err != nil {
948+ return fmt .Errorf ("failed to do patch request: %v" , err )
949+ }
950+ defer resp .Body .Close ()
951+ // We will consider ONLY 204 as success
952+ if resp .StatusCode != http .StatusNoContent {
953+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
954+ "expected %v response, got %v" ,
955+ http .StatusNoContent , resp .StatusCode ))
956+ }
957+ return nil
958+ }
959+
960+ // DeleteKeyValuePair deletes the key/value pair for a key in a given HTTP zone.
961+ func (client * NginxClient ) DeleteKeyValuePair (zone string , key string ) error {
962+ return client .deleteKeyValuePair (zone , key , HTTP )
963+ }
964+
965+ // DeleteStreamKeyValuePair deletes the key/value pair for a key in a given Stream zone.
966+ func (client * NginxClient ) DeleteStreamKeyValuePair (zone string , key string ) error {
967+ return client .deleteKeyValuePair (zone , key , Stream )
968+ }
969+
970+ // To delete a key/value pair you set the value to null via the API,
971+ // then NGINX+ will delete the key.
972+ func (client * NginxClient ) deleteKeyValuePair (zone string , key string , stream bool ) error {
973+ base := "http"
974+ if stream {
975+ base = "stream"
976+ }
977+ path := fmt .Sprintf ("%v/keyvals" , base )
978+ url := fmt .Sprintf ("%v/%v/%v" , client .apiEndpoint , APIVersion , path )
979+ if zone != "" {
980+ url = fmt .Sprintf ("%v/%v" , url , zone )
981+ } else {
982+ return fmt .Errorf ("zone required" )
983+ }
984+
985+ // map[string]string can't have a nil value so we use a different type here.
986+ keyval := make (map [string ]interface {})
987+ keyval [key ] = nil
988+
989+ jsonInput , err := json .Marshal (keyval )
990+ if err != nil {
991+ return fmt .Errorf ("failed to marshall input: %v" , err )
992+ }
993+
994+ req , err := http .NewRequest (http .MethodPatch , url , bytes .NewBuffer (jsonInput ))
995+ if err != nil {
996+ return fmt .Errorf ("failed to create a patch request: %v" , err )
997+ }
998+ req .Header .Set ("Content-Type" , "application/json" )
999+
1000+ resp , err := client .httpClient .Do (req )
1001+ if err != nil {
1002+ return fmt .Errorf ("failed to do patch request: %v" , err )
1003+ }
1004+ defer resp .Body .Close ()
1005+
1006+ // Expect status 204
1007+ if resp .StatusCode != http .StatusNoContent {
1008+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
1009+ "expected %v response, got %v" ,
1010+ http .StatusNoContent , resp .StatusCode ))
1011+ }
1012+ return nil
1013+ }
1014+
1015+ // DeleteKeyValPairs deletes all the key-value pairs in a given HTTP zone.
1016+ func (client * NginxClient ) DeleteKeyValPairs (zone string ) error {
1017+ return client .deleteKeyValPairs (zone , HTTP )
1018+ }
1019+
1020+ // DeleteStreamKeyValPairs deletes all the key-value pairs in a given Stream zone.
1021+ func (client * NginxClient ) DeleteStreamKeyValPairs (zone string ) error {
1022+ return client .deleteKeyValPairs (zone , Stream )
1023+ }
1024+
1025+ func (client * NginxClient ) deleteKeyValPairs (zone string , stream bool ) error {
1026+ base := "http"
1027+ if stream {
1028+ base = "stream"
1029+ }
1030+ if zone == "" {
1031+ return fmt .Errorf ("zone required" )
1032+ }
1033+ path := fmt .Sprintf ("%v/keyvals/%v" , base , zone )
1034+ url := fmt .Sprintf ("%v/%v/%v" , client .apiEndpoint , APIVersion , path )
1035+
1036+ req , err := http .NewRequest (http .MethodDelete , url , nil )
1037+ if err != nil {
1038+ return fmt .Errorf ("failed to create a delete request: %v" , err )
1039+ }
1040+
1041+ resp , err := client .httpClient .Do (req )
1042+ if err != nil {
1043+ return fmt .Errorf ("failed to do delete request: %v" , err )
1044+ }
1045+ defer resp .Body .Close ()
1046+
1047+ // expect status 204
1048+ if resp .StatusCode != http .StatusNoContent {
1049+ return createResponseMismatchError (resp .Body ).Wrap (fmt .Sprintf (
1050+ "expected %v response, got %v" ,
1051+ http .StatusNoContent , resp .StatusCode ))
1052+ }
1053+ return nil
1054+ }
0 commit comments