@@ -549,6 +549,8 @@ public:
549
549
" run the server" )
550
550
.addSubCommand (" compile" , KJ_BIND_METHOD (*this , getCompile),
551
551
" create a self-contained binary" )
552
+ .addSubCommand (" measure" , KJ_BIND_METHOD (*this , getMeasure),
553
+ " measure the provided worker configuration" )
552
554
.addSubCommand (" test" , KJ_BIND_METHOD (*this , getTest),
553
555
" run unit tests" )
554
556
.build ();
@@ -558,10 +560,13 @@ public:
558
560
// "explain": Produces human-friendly description of the config.
559
561
} else {
560
562
// We already have a config, meaning this must be a compiled binary.
561
- auto builder = kj::MainBuilder (context, getVersionString (),
562
- " Serve requests based on the compiled config." ,
563
- " This binary has an embedded configuration." );
564
- return addServeOptions (builder);
563
+ return kj::MainBuilder (context, getVersionString (),
564
+ " Runs the Workers JavaScript/Wasm runtime." )
565
+ .addSubCommand (" serve" , KJ_BIND_METHOD (*this , getServe),
566
+ " run the server" )
567
+ .addSubCommand (" measure" , KJ_BIND_METHOD (*this , getMeasure),
568
+ " measure the provided worker configuration" )
569
+ .build ();
565
570
}
566
571
}
567
572
@@ -688,6 +693,16 @@ public:
688
693
.build ();
689
694
}
690
695
696
+ kj::MainFunc getMeasure () {
697
+ return kj::MainBuilder (context, getVersionString (),
698
+ " Measures the provided worker config and outputs the hash." ,
699
+ " Loads a worker's code and config in the same way as would be done by the "
700
+ " `workerd.createWorker` method, hashes the full config, and returns the hash." )
701
+ .expectArg (" <config-file>" , CLI_METHOD (parseWorkerConfigFile))
702
+ .callAfterParsing (CLI_METHOD (measure))
703
+ .build ();
704
+ }
705
+
691
706
void addImportPath (kj::StringPtr pathStr) {
692
707
auto path = fs->getCurrentPath ().evalNative (pathStr);
693
708
if (fs->getRoot ().tryOpenSubdir (path) != kj::none) {
@@ -811,6 +826,19 @@ public:
811
826
}
812
827
813
828
void parseConfigFile (kj::StringPtr pathStr) {
829
+ config = parseCapnpConfig<config::Config>(pathStr);
830
+ // We'll fail at getConfig() if there are multiple top level Config objects.
831
+ // The error message says that you have to specify which config to use, but
832
+ // it's not clear that there is any mechanism to do that.
833
+ util::Autogate::initAutogate (getConfig ().getAutogates ());
834
+ }
835
+
836
+ void parseWorkerConfigFile (kj::StringPtr pathStr) {
837
+ workerConfig = parseCapnpConfig<config::Worker>(pathStr);
838
+ }
839
+
840
+ template <typename T>
841
+ kj::Maybe<typename T::Reader> parseCapnpConfig (kj::StringPtr pathStr) {
814
842
if (pathStr == " -" ) {
815
843
// Read from stdin.
816
844
@@ -826,8 +854,8 @@ public:
826
854
#else
827
855
auto reader = kj::heap<capnp::StreamFdMessageReader>(STDIN_FILENO, CONFIG_READER_OPTIONS);
828
856
#endif
829
- config = reader->getRoot <config::Config>();
830
857
configOwner = kj::mv (reader);
858
+ return reader->getRoot <T>();
831
859
} else {
832
860
// Read file from disk.
833
861
auto path = fs->getCurrentPath ().evalNative (pathStr);
@@ -840,11 +868,11 @@ public:
840
868
mapping.size () / sizeof (capnp::word));
841
869
auto reader = kj::heap<capnp::FlatArrayMessageReader>(words, CONFIG_READER_OPTIONS)
842
870
.attach (kj::mv (mapping));
843
- config = reader->getRoot <config::Config>();
844
871
configOwner = kj::mv (reader);
872
+ return reader->getRoot <T>();
845
873
} else {
846
874
// Interpret as schema file.
847
- schemaParser.loadCompiledTypeAndDependencies <config::Config >();
875
+ schemaParser.loadCompiledTypeAndDependencies <T >();
848
876
849
877
parsedSchema = schemaParser.parseFile (
850
878
kj::heap<SchemaFileImpl>(fs->getRoot (), fs->getCurrentPath (),
@@ -857,18 +885,14 @@ public:
857
885
auto constSchema = nested.asConst ();
858
886
auto type = constSchema.getType ();
859
887
if (type.isStruct () &&
860
- type.asStruct ().getProto ().getId () == capnp::typeId<config::Config >()) {
861
- topLevelConfigConstants. add (constSchema );
888
+ type.asStruct ().getProto ().getId () == capnp::typeId<T >()) {
889
+ return constSchema. as <T>( );
862
890
}
863
891
}
864
892
}
893
+ return kj::none;
865
894
}
866
895
}
867
-
868
- // We'll fail at getConfig() if there are multiple top level Config objects.
869
- // The error message says that you have to specify which config to use, but
870
- // it's not clear that there is any mechanism to do that.
871
- util::Autogate::initAutogate (getConfig ().getAutogates ());
872
896
}
873
897
874
898
void setConstName (kj::StringPtr name) {
@@ -1075,6 +1099,15 @@ public:
1075
1099
}
1076
1100
}
1077
1101
1102
+ void measure () {
1103
+ if (hadErrors) context.exit ();
1104
+ auto measurement = workerd::server::measureConfig (
1105
+ KJ_UNWRAP_OR (workerConfig, CLI_ERROR (" no worker config provided" )));
1106
+ auto measurementHex = kj::encodeHex (measurement);
1107
+ kj::FdOutputStream out{STDOUT_FILENO};
1108
+ out.write (measurementHex.asBytes ().begin (), measurementHex.size ());
1109
+ }
1110
+
1078
1111
[[noreturn]] void serve () noexcept {
1079
1112
serveImpl ([&](jsg::V8System& v8System, config::Config::Reader config) {
1080
1113
#if _WIN32
@@ -1164,6 +1197,7 @@ private:
1164
1197
1165
1198
kj::Own<void > configOwner; // backing object for `config`, if it's not `schemaParser`.
1166
1199
kj::Maybe<config::Config::Reader> config;
1200
+ kj::Maybe<config::Worker::Reader> workerConfig;
1167
1201
1168
1202
kj::Vector<int > inheritedFds;
1169
1203
0 commit comments