diff --git a/obs/error.go b/obs/error.go new file mode 100644 index 0000000..f5d703e --- /dev/null +++ b/obs/error.go @@ -0,0 +1,38 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package obs + +import ( + "encoding/xml" + "fmt" +) + +type Status struct { + XMLName xml.Name `json:"status" xml:"status"` + Code string `json:"code" xml:"code,attr"` + Summary string `json:"summary" xml:"summary"` +} + +type APIError struct { + HTTPStatusCode int + OBSStatusCode string + Message string +} + +func (e *APIError) Error() string { + return fmt.Sprintf("HTTP status %d: %s (%s)", e.HTTPStatusCode, e.OBSStatusCode, e.Message) +} diff --git a/obs/project.go b/obs/project.go index 2682f24..f4ef273 100644 --- a/obs/project.go +++ b/obs/project.go @@ -17,7 +17,12 @@ limitations under the License. package obs import ( + "bytes" "encoding/xml" + "fmt" + "log" + "net/http" + "net/url" ) type Project struct { @@ -36,6 +41,7 @@ type Project struct { } type Client struct { + Client http.Client Username string Password string APIURL string @@ -76,8 +82,8 @@ const ( type Repository struct { Repository string `json:"name" xml:"name,attr"` - Architectures []string `json:"arch" xml:"arch"` - ReleaseTargets []ReleaseTarget `json:"releasetarget,omitempty" xml:"releasetarget,omitempty"` + Architectures []string `json:"architectures" xml:"arch"` + ReleaseTargets []ReleaseTarget `json:"releaseTargets,omitempty" xml:"releasetarget,omitempty"` Paths []RepositoryPath `json:"path,omitempty" xml:"path,omitempty"` } @@ -91,3 +97,102 @@ type RepositoryPath struct { Project string `json:"project" xml:"project,attr"` Repository string `json:"repository" xml:"repository,attr"` } + +func (c Client) CreateProject(project *Project) error { + xmlData, err := xml.MarshalIndent(project, "", " ") + if err != nil { + return err + } + + urlPath, err := url.JoinPath(c.APIURL, "source", project.Name, "_meta") + if err != nil { + log.Fatal(err) + } + + req, err := http.NewRequest("PUT", urlPath, bytes.NewBuffer(xmlData)) + if err != nil { + return &APIError{ + HTTPStatusCode: 0, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to generate a new request: %v", err), + } + } + + req.SetBasicAuth(c.Username, c.Password) + req.Header.Set("Accept", "application/xml; charset=utf-8") + + resp, err := c.Client.Do(req) + if err != nil { + return &APIError{ + HTTPStatusCode: 0, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to make a new request: %v", err), + } + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var status Status + if err := xml.NewDecoder(resp.Body).Decode(&status); err != nil { + return &APIError{ + HTTPStatusCode: resp.StatusCode, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to decode the error response: %v", err), + } + } + + return &APIError{ + HTTPStatusCode: resp.StatusCode, + OBSStatusCode: status.Code, + Message: status.Summary, + } + } + return nil +} + +func (c Client) DeleteProject(project *Project) error { + urlPath, err := url.JoinPath(c.APIURL, "source", project.Name) + if err != nil { + log.Fatal(err) + } + + req, err := http.NewRequest("DELETE", urlPath, nil) + if err != nil { + return &APIError{ + HTTPStatusCode: 0, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to generate a new request: %v", err), + } + } + + req.SetBasicAuth(c.Username, c.Password) + req.Header.Set("Accept", "application/xml; charset=utf-8") + + resp, err := c.Client.Do(req) + if err != nil { + return &APIError{ + HTTPStatusCode: 0, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to make a new request: %v", err), + } + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var status Status + if err := xml.NewDecoder(resp.Body).Decode(&status); err != nil { + return &APIError{ + HTTPStatusCode: resp.StatusCode, + OBSStatusCode: "", + Message: fmt.Sprintf("failed to decode the error response: %v", err), + } + } + + return &APIError{ + HTTPStatusCode: resp.StatusCode, + OBSStatusCode: status.Code, + Message: status.Summary, + } + } + return nil +}