summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hscript/meta.cc38
-rw-r--r--hscript/meta.hh10
-rw-r--r--hscript/script.cc82
-rw-r--r--tests/fixtures/0055-repository-basic.installfile7
-rw-r--r--tests/fixtures/0056-repository-invalid.installfile6
-rw-r--r--tests/spec/validator.rb13
6 files changed, 147 insertions, 9 deletions
diff --git a/hscript/meta.cc b/hscript/meta.cc
index faec607..dc29b0c 100644
--- a/hscript/meta.cc
+++ b/hscript/meta.cc
@@ -19,7 +19,7 @@
using namespace Horizon::Keys;
Key *Hostname::parseFromData(const std::string data, int lineno, int *errors,
- int *warnings) {
+ int *) {
std::regex valid_re("[A-Za-z0-9-_.]*");
if(!std::regex_match(data, valid_re)) {
if(errors) *errors += 1;
@@ -142,3 +142,39 @@ Key *PkgInstall::parseFromData(const std::string data, int lineno, int *errors,
}
return new PkgInstall(lineno, all_pkgs);
}
+
+Key *Repository::parseFromData(const std::string data, int lineno, int *errors,
+ int *) {
+ if(data.empty() || (data[0] != '/' && data.compare(0, 4, "http"))) {
+ if(errors) *errors += 1;
+ output_error("installfile:" + std::to_string(lineno),
+ "repository: must be absolute path or HTTP(S) URL");
+ return nullptr;
+ }
+ return new Repository(lineno, data);
+}
+
+bool Repository::validate(ScriptOptions) const {
+ /* TODO XXX: Ensure URL is accessible if networking is available */
+ return true;
+}
+
+bool Repository::execute(ScriptOptions opts) const {
+ /* Runner.Execute.repository. */
+ if(opts.test(Simulate)) {
+ output_info("installfile:" + std::to_string(this->lineno()),
+ "repository: write '" + this->value() +
+ "' to /etc/apk/repositories");
+ } else {
+ std::ofstream repo_f("/target/etc/apk/repositories",
+ std::ios_base::ate);
+ if(!repo_f) {
+ output_error("installfile:" + std::to_string(this->lineno()),
+ "repository: could not open /etc/apk/repositories "
+ "for writing");
+ return false;
+ }
+ repo_f << this->value() << std::endl;
+ }
+ return true;
+}
diff --git a/hscript/meta.hh b/hscript/meta.hh
index 3574a6f..846158d 100644
--- a/hscript/meta.hh
+++ b/hscript/meta.hh
@@ -57,7 +57,15 @@ class Firmware : public BooleanKey {
class Timezone : public StringKey {
};
-class Repository : public Key {
+class Repository : public StringKey {
+private:
+ Repository(int _line, const std::string my_url) :
+ StringKey(_line, my_url) {}
+public:
+ static Key *parseFromData(const std::string data, int lineno, int *errors,
+ int *warnings);
+ bool validate(ScriptOptions) const override;
+ bool execute(ScriptOptions) const override;
};
class SigningKey : public Key {
diff --git a/hscript/script.cc b/hscript/script.cc
index 0583a3c..e92a39f 100644
--- a/hscript/script.cc
+++ b/hscript/script.cc
@@ -81,6 +81,9 @@ struct Script::ScriptPrivate {
/*! Network addressing configuration */
std::vector< std::unique_ptr<Horizon::Keys::NetAddress> > addresses;
+ /*! APK repositories */
+ std::vector< std::unique_ptr<Horizon::Keys::Repository> > repos;
+
/*! Store +key_obj+ representing the key +key_name+.
* @param key_name The name of the key that is being stored.
* @param key_obj The Key object associated with the key.
@@ -157,6 +160,12 @@ struct Script::ScriptPrivate {
);
this->addresses.push_back(std::move(addr));
return true;
+ } else if(key_name == "repository") {
+ std::unique_ptr<Keys::Repository> repo(
+ dynamic_cast<Keys::Repository *>(key_obj)
+ );
+ this->repos.push_back(std::move(repo));
+ return true;
} else {
return false;
}
@@ -373,6 +382,47 @@ bool Script::validate() const {
}
}
+ if(this->internal->repos.size() == 0) {
+ Keys::Repository *sys_key = dynamic_cast<Keys::Repository *>(
+ Horizon::Keys::Repository::parseFromData(
+ "https://distfiles.adelielinux.org/adelie/stable/system", 0,
+ nullptr, nullptr
+ )
+ );
+ if(!sys_key) {
+ output_error("internal", "failed to create default system repository");
+ return false;
+ }
+ std::unique_ptr<Keys::Repository> sys_repo(sys_key);
+ this->internal->repos.push_back(std::move(sys_repo));
+ Keys::Repository *user_key = dynamic_cast<Keys::Repository *>(
+ Horizon::Keys::Repository::parseFromData(
+ "https://distfiles.adelielinux.org/adelie/stable/user", 0,
+ nullptr, nullptr
+ )
+ );
+ if(!user_key) {
+ output_error("internal", "failed to create default user repository");
+ return false;
+ }
+ std::unique_ptr<Keys::Repository> user_repo(user_key);
+ this->internal->repos.push_back(std::move(user_repo));
+ }
+
+ for(auto &repo : this->internal->repos) {
+ if(!repo->validate(this->opts)) {
+ failures++;
+ }
+ }
+
+ if(this->internal->repos.size() > 10) {
+ failures++;
+ output_error("installfile:" +
+ std::to_string(this->internal->repos[11]->lineno()),
+ "repository: too many repositories specified",
+ "You may only specify up to 10 repositories.");
+ }
+
output_log("validator", "0", "installfile",
std::to_string(failures) + " failure(s).", "");
return (failures == 0);
@@ -392,15 +442,33 @@ bool Script::execute() const {
return false;
}
- output_step_start("metadata");
- if(!this->internal->hostname->execute(opts) ||
- !this->internal->rootpw->execute(opts)) {
- output_error("metadata", "The HorizonScript failed to execute",
- "Check the log file for more details.");
+#define EXECUTE_FAILURE(phase) \
+ output_error("pre-metadata", "The HorizonScript failed to execute",\
+ "Check the log file for more details.")
+
+ output_step_start("pre-metadata");
+ if(!this->internal->hostname->execute(opts)) {
+ EXECUTE_FAILURE("pre-metadata");
+ return false;
+ }
+ output_step_end("pre-metadata");
+
+ output_step_start("pkgdb");
+ for(auto &repo : this->internal->repos) {
+ if(!repo->execute(opts)) {
+ EXECUTE_FAILURE("pkgdb");
+ return false;
+ }
+ }
+ output_step_end("pkgdb");
+
+ output_step_start("post-metadata");
+ if(!this->internal->rootpw->execute(opts)) {
+ EXECUTE_FAILURE("post-metadata");
return false;
}
- output_step_end("metadata");
- return false;
+ output_step_end("post-metadata");
+ return true;
}
}
diff --git a/tests/fixtures/0055-repository-basic.installfile b/tests/fixtures/0055-repository-basic.installfile
new file mode 100644
index 0000000..b7d8d7f
--- /dev/null
+++ b/tests/fixtures/0055-repository-basic.installfile
@@ -0,0 +1,7 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+repository https://distfiles.adelielinux.org/adelie/current/system
+repository https://distfiles.adelielinux.org/adelie/current/user
diff --git a/tests/fixtures/0056-repository-invalid.installfile b/tests/fixtures/0056-repository-invalid.installfile
new file mode 100644
index 0000000..0b393a5
--- /dev/null
+++ b/tests/fixtures/0056-repository-invalid.installfile
@@ -0,0 +1,6 @@
+network false
+hostname test.machine
+pkginstall adelie-base
+rootpw $6$gumtLGmHwOVIRpQR$2M9PUO24hy5mofzWWf9a.YLbzOgOlUby1g0hDj.wG67E2wrrvys59fq02PPdxBdbgkLZFtjfEx6MHZwMBamwu/
+mount /dev/sda1 /
+repository ftp://anonymous:password@unsupported.ftp.network/pub/adelie/current/system
diff --git a/tests/spec/validator.rb b/tests/spec/validator.rb
index 4ba83cf..11fd4c4 100644
--- a/tests/spec/validator.rb
+++ b/tests/spec/validator.rb
@@ -337,6 +337,19 @@ RSpec.describe 'HorizonScript Validation Utility', :type => :aruba do
expect(last_command_started).to have_output(/error: .*netaddress.*addresses/)
end
end
+ context "for 'repository' key" do
+ it "succeeds with basic repositories" do
+ use_fixture '0055-repository-basic.installfile'
+ run_validate
+ expect(last_command_started).to have_output(PARSER_SUCCESS)
+ expect(last_command_started).to have_output(VALIDATOR_SUCCESS)
+ end
+ it "fails with invalid repository URL" do
+ use_fixture '0056-repository-invalid.installfile'
+ run_validate
+ expect(last_command_started).to have_output(/error: .*repository/)
+ end
+ end
end
context "unique keys" do
# Runner.Validate.network.