github.com/smugmug/godynamo@v0.0.0-20151122084750-7913028f6623/endpoints/describe_table/describe_table.go (about) 1 // Support for the DynamoDB DescribeTable endpoint. 2 // 3 // example use: 4 // 5 // tests/create_table-livestest.go, which contains a DescribeTable invocation 6 // 7 package describe_table 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 "github.com/smugmug/godynamo/authreq" 14 "github.com/smugmug/godynamo/aws_const" 15 "github.com/smugmug/godynamo/conf" 16 ep "github.com/smugmug/godynamo/endpoint" 17 "github.com/smugmug/godynamo/types/attributedefinition" 18 "github.com/smugmug/godynamo/types/globalsecondaryindex" 19 "github.com/smugmug/godynamo/types/keydefinition" 20 "github.com/smugmug/godynamo/types/localsecondaryindex" 21 "github.com/smugmug/godynamo/types/provisionedthroughput" 22 "net/http" 23 "time" 24 ) 25 26 const ( 27 ENDPOINT_NAME = "DescribeTable" 28 DESCTABLE_ENDPOINT = aws_const.ENDPOINT_PREFIX + ENDPOINT_NAME 29 ACTIVE = "ACTIVE" 30 ) 31 32 type DescribeTable struct { 33 TableName string 34 } 35 36 // Describe is an alias for backwards compatibility 37 type Describe DescribeTable 38 39 type Request DescribeTable 40 41 func NewDescribeTable() *DescribeTable { 42 d := new(DescribeTable) 43 return d 44 } 45 46 type Response struct { 47 Table struct { 48 AttributeDefinitions attributedefinition.AttributeDefinitions 49 CreationDateTime float64 50 GlobalSecondaryIndexes []globalsecondaryindex.GlobalSecondaryIndexDesc 51 ItemCount uint64 52 KeySchema keydefinition.KeySchema 53 LocalSecondaryIndexes []localsecondaryindex.LocalSecondaryIndexDesc 54 ProvisionedThroughput provisionedthroughput.ProvisionedThroughputDesc 55 TableName string 56 TableSizeBytes uint64 57 TableStatus string 58 } 59 } 60 61 func NewResponse() *Response { 62 r := new(Response) 63 r.Table.AttributeDefinitions = make(attributedefinition.AttributeDefinitions, 0) 64 r.Table.GlobalSecondaryIndexes = make([]globalsecondaryindex.GlobalSecondaryIndexDesc, 0) 65 r.Table.KeySchema = make(keydefinition.KeySchema, 0) 66 r.Table.LocalSecondaryIndexes = make([]localsecondaryindex.LocalSecondaryIndexDesc, 0) 67 return r 68 } 69 70 type StatusResult struct { 71 StatusResult bool 72 } 73 74 // These implementations of EndpointReq use a parameterized conf. 75 76 func (describe_table *DescribeTable) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) { 77 if describe_table == nil { 78 return nil, 0, errors.New("describe_table.(DescribeTable)EndpointReqWithConf: receiver is nil") 79 } 80 if !conf.IsValid(c) { 81 return nil, 0, errors.New("describe_table.EndpointReqWithConf: c is not valid") 82 } 83 // returns resp_body,code,err 84 reqJSON, json_err := json.Marshal(describe_table) 85 if json_err != nil { 86 return nil, 0, json_err 87 } 88 return authreq.RetryReqJSON_V4WithConf(reqJSON, DESCTABLE_ENDPOINT, c) 89 } 90 91 func (describe *Describe) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) { 92 if describe == nil { 93 return nil, 0, errors.New("describe_table.(Describe)EndpointReqWithConf: receiver is nil") 94 } 95 describe_table := DescribeTable(*describe) 96 return describe_table.EndpointReqWithConf(c) 97 } 98 99 func (req *Request) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) { 100 if req == nil { 101 return nil, 0, errors.New("describe_table.(Request)EndpointReqWithConf: receiver is nil") 102 } 103 describe_table := DescribeTable(*req) 104 return describe_table.EndpointReqWithConf(c) 105 } 106 107 // These implementations of EndpointReq use the global conf. 108 109 func (describe_table *DescribeTable) EndpointReq() ([]byte, int, error) { 110 if describe_table == nil { 111 return nil, 0, errors.New("describe_table.(DescribeTable)EndpointReq: receiver is nil") 112 } 113 return describe_table.EndpointReqWithConf(&conf.Vals) 114 } 115 116 func (describe *Describe) EndpointReq() ([]byte, int, error) { 117 if describe == nil { 118 return nil, 0, errors.New("describe_table.(Describe)EndpointReq: receiver is nil") 119 } 120 describe_table := DescribeTable(*describe) 121 return describe_table.EndpointReqWithConf(&conf.Vals) 122 } 123 124 func (req *Request) EndpointReq() ([]byte, int, error) { 125 if req == nil { 126 return nil, 0, errors.New("describe_table.(Request)EndpointReq: receiver is nil") 127 } 128 describe_table := DescribeTable(*req) 129 return describe_table.EndpointReqWithConf(&conf.Vals) 130 } 131 132 // PollTableStatusWithConf allows the caller to poll a table for a specific status. 133 func PollTableStatusWithConf(tablename string, status string, tries int, c *conf.AWS_Conf) (bool, error) { 134 if !conf.IsValid(c) { 135 return false, errors.New("describe_table.PollTableStatusWithConf: c is not valid") 136 } 137 // aws docs informs us to poll the describe endpoint until the table 138 // "status" is status for this tablename 139 wait := time.Duration(2 * time.Second) 140 141 for i := 0; i < tries; i++ { 142 active, err := IsTableStatusWithConf(tablename, status, c) 143 if err != nil { 144 e := fmt.Sprintf("describe_table.PollStatus:%s", 145 err.Error()) 146 return false, errors.New(e) 147 } 148 if active { 149 return active, nil 150 } 151 time.Sleep(wait) // wait for table to become ACTIVE 152 } 153 return false, nil 154 } 155 156 // PollTableStatus is the same as PollTableStatusWithConf but uses the global conf.Vals. 157 func PollTableStatus(tablename string, status string, tries int) (bool, error) { 158 return PollTableStatusWithConf(tablename, status, tries, &conf.Vals) 159 } 160 161 // IsTableStatusWithConf will test the equality status of a table. 162 func IsTableStatusWithConf(tablename string, status string, c *conf.AWS_Conf) (bool, error) { 163 if !conf.IsValid(c) { 164 return false, errors.New("describe_table.IsTableStatusWithConf: c is not valid") 165 } 166 d := ep.Endpoint(&DescribeTable{TableName: tablename}) 167 s_resp, s_code, s_err := authreq.RetryReq_V4WithConf(d, DESCTABLE_ENDPOINT, c) 168 if s_err != nil { 169 e := fmt.Sprintf("describe_table.IsTableStatus: "+ 170 "check on %s err %s", 171 tablename, s_err.Error()) 172 // if not a 500 problem, don't retry 173 if !ep.ServerErr(s_code) { 174 return false, errors.New(e) 175 } 176 } 177 if s_resp != nil && s_code == http.StatusOK { 178 var resp_json Response 179 um_err := json.Unmarshal([]byte(s_resp), &resp_json) 180 if um_err != nil { 181 um_msg := fmt.Sprintf("describe_table.IsTableStatus:"+ 182 "cannot unmarshal %s, err: %s\ncheck "+ 183 "table creation of %s manually", 184 s_resp, um_err.Error(), tablename) 185 return false, errors.New(um_msg) 186 } 187 return (resp_json.Table.TableStatus == status), nil 188 } 189 e := fmt.Sprintf("describe_table.IsTableStatus:does %s exist?", tablename) 190 return false, errors.New(e) 191 } 192 193 // IsTableStatus is the same as IsTableStatusWithConf but uses the global conf.Vals. 194 func IsTableStatus(tablename string, status string) (bool, error) { 195 return IsTableStatusWithConf(tablename, status, &conf.Vals) 196 } 197 198 // TableExistsWithconf test for table exists: exploit the fact that aws reports 4xx for tables that don't exist. 199 func (desc DescribeTable) TableExistsWithConf(c *conf.AWS_Conf) (bool, error) { 200 if !conf.IsValid(c) { 201 return false, errors.New("describe_table.TableExistsWithConf: c is not valid") 202 } 203 _, code, err := desc.EndpointReqWithConf(c) 204 if err != nil { 205 e := fmt.Sprintf("describe_table.TableExistsWithConf "+ 206 "%s", err.Error()) 207 return false, errors.New(e) 208 } 209 return (code == http.StatusOK), nil 210 } 211 212 // TableExists is the same as TableExistsWithConf but uses the global conf.Vals. 213 func (desc DescribeTable) TableExists() (bool, error) { 214 return desc.TableExistsWithConf(&conf.Vals) 215 }